This post explains how to write to and read from a BRAM IP using HLS codes. I will write two different HLS codes to write into and read from a BRAM IP.

If you want to learn more about the BRAM in HLS and all the design details in this post, please refer to my online course, “High-Level Synthesis for FPGA, Part 3 – Advanced“.

I assume that there is a dual-port BRAM IP inside the FPGA. So we should develop two HLS IPs to exchange data with this BRAM. The first IP writes to the BRAM, and the second reads the data from that; then, it adds them together and sends the result out to be shown on LEDs available on the Basys3 board.

The following diagram shows the final IPs instantiated in a Vivado project.

HLS Code

The following figure shows the HLS code for the write IP. The write HLS-IP code has only one array argument for sending data out. A for-loop assigns data to the A array elements, which later will be written into the BRAM IP. After synthesis, the ap_memory is assigned to the A argument, adding memory access signals to the IP. The figure also shows the added signals to the generated IP for writing data into memory. These signals are A_ce0, Awe0, A_address0 and A_d0.

The read HLS code consists of two arguments; the A array is used to read data from the BRAM IP inside the FPGA, and the B reference variable is used to send out the sum of the read data to be shown on LEDs. 

The top-function body consists of a for-loop that reads data element-by-element and accumulates them in the local t variable. After finishing the loop iterations, the resulting number is written into the B reference argument.

The A array argument utilises the ap_memory interface to provide the corresponding BRAM access signals.

Write IP Vitis_HLS Project 

Create a Vitis-HLS project and name it “array_write-vitishls.” Add the design and testbench files to the created project. The design header file defines two parameters N and SIZE. N is 32, and SIZE is 4.

If you perform the C-Simulation, you should see four values printed on the screen. Then you can run the synthesis process to generate the corresponding RTL code. The synthesis report shows that the latency of the function is 5.

Read IP Vitis_HLS Project 

To design a module that reads the data from memory and then adds them together, create another Vitis-HLS project named array_read-vitishls. Add the design and testbench files to the project.

Open the design source file. The top function uses a for-loop with a trip count of 4 to read the data elements from the input A array and adds them together. Finally, the function returns the result via the B variable.

If you perform the C simulation, you should get 10 as the sum of all four data elements in the A array.

Now, perform the synthesis process. The total latency of the top function is 9 cycles.

Read/Write Vivado Project

Let’s examine this design in a Vivado project. For this purpose, create a Vivado project with the “array_rw-Vivado” name. Then create a block design and add the generated HLS IPs into the Vivado repository. Then add the array_write IP into the design area. Click on the “Run Connection Automation” link. Select the ap_clk option on the left and press OK. Then add the array_read IP and click again on the “Run Connection Automation” link. Select all the options on the left and press OK.

Then add the debouncer and pulse generator IPs to provide a pulse signal for the ap_start on the array_write IP. After that connect the ap_done of the array_write IP to the ap_start of the array_red IP.

Search for the Block Memory Generator IP and add that to the design, then double-click on the added IP to re-customise that.

Choose Stand Alone as the mode. Then select the True Dual Port RAM option as the memory type. Change the write and read widths from 32 to 8. Deselect the Primitive Output Register option. This optional output register increases the read latency from 1 to 2. Deselect the Primitive Output Register option for the B port as well. Now we have to connect the two IPs to the re-customised BRAM. As the top BRAM memory port is used for writing, douta is not connected. And as the bottom, BRAM memory port is used for reading dinb, and wen is not connected. Make the B port external.

Now you can manually add a System ILA IP for debugging. Alternatively, you can use the debug option in Vivado to add the ILA IP automatically. Let’s add an ILA IP to connect that to a couple of signals in the design. Modify the external port names.

Program the board and examine the design functionality. In the Trigger Setup view, click on the plus icon. Select the pulse generator probe as the trigger signal. Change the Value to 1. Now in the status view, click on the run icon. The design is waiting to press the UP push button. Press the UP push button, then check the logic value on the LEDs and the signal waveform in the Vivado.

If you want to learn more about the BRAM in HLS and all the design details in this post, please refer to my online course, “High-Level Synthesis for FPGA, Part 3 – Advanced“.

Leave a Reply