Memory Initialization using HesDebug API from HDL testbench in Acceleration Mode

Introduction

Memories that are used in RTL design usually must be initialized before performing actual tests on them. If a pure software model is used for simulation then it is a trivial task. For example, there are $readmem[h|b] and $writemem[h|b] system tasks dedicated for this purpose in Verilog. However, if hardware acceleration is used, then memory is located inside the hardware and the simulator does not have direct access to it.

Aldec provides HesDebug API that can be used to get access to memories inside the HES emulator. In this article we will see how to invoke HesDebug API functions from the Verilog/SystemVerilog testbench in the acceleration mode, and how to use them to access memory modules.

NOTE: Using HesDebug API in the acceleration mode is available since the HES-DVM.2013.11 release.

You can download the complete example design: Mem_Init_HESDebugAPI_AccelMode

Preparing the DVM Project

First, the DVM project should be created. The DVM memory model should be mapped to the design memory, which we are going to work with. To do so, mark the memory module instance as a memory using the Module | External/Internal popup menu button from the Design tab in HES-DVM. After the design is added to the HES board, you will be able to select the particular memory model in the Devices tab. Please refer to the product documentation for additional information.

For the sample design, the whole DVM project setup process can be performed via console commands using the ./dvm/rundvm.do file in the example design.

After the HES files are generated, you have to check if the correct path to the hes.xml file is specified in the top design unit wrapper. To do so, go to the <DVMPROJ_DIR>/HES/simul directory and open the <LIBRARY>_<TOP_MODULE>_HES.v file. Find the `hes directive in this file and ensure that the “-ddxml“ argument points to the correct hes.xml file. If it is missing, add the argument. For the sample design it should be: -ddxml \"./HES/simul/hes.xml\". The path to the hes.xml file is specified relatively to the directory where the compilation is executed. The full `hes directive for our example will look as follows:

`hes("-map \"./HES/simul/mapping.map\" -Std2HES \"10\" -gen -verilog -mod top_sram_aux_sim0 -ddxml \"./HES/simul/hes.xml\"")

Accessing the HesDebug API

The HesDebug API is a C++ API, so custom DPI functions should be used to access them. First, declare the external function in the testbench. In the sample design, Verilog interfaces for the DPI functions are declared in the ./src/dpi/memhes.v file:

import "DPI-C" context function void readMemHesH(input string file_name, input string memory_path);

task readmemhesh;
	input string file_name;
	input string memory_path;
	readMemHesH(file_name, memory_path);
endtask

Here, the readMemHesH function is an external function implemented using the C++ code. It accepts two parameters: file_name – file with the values that should be written to the memory, and memory_path – hierarchical instance path to the memory module. The instance path is used as a unique memory identifier. The readmemhesh task is a Verilog task that can be called in the testbench. On the C++ side, function declarations are available in the ./src/dpi/memhes.h file:

#ifdef __cplusplus
#define DPI_LINK_DECL  extern "C" 
#else
#define DPI_LINK_DECL 
#endif

DPI_LINK_DECL void readMemHesH(const char * file_name, const char * memory_path);

The readMemHesH function has the same name and the same parameters as its declaration on the Verilog side. The DPI_LINK_DECL macro is used to add extern "C" before the function declaration if it is compiled with the C++ compiler. Thus, it is exported in the C format, required for the DPI functions. You have to include the file "HesDebugApi.h" located in the <DVM_INSTALL_DIR>/include directory to gain access to the API functions. It provides a set of functions that can be used to control emulation of the HES board. In this article we will refer only to a part of it.

Initializing Memory

The read/writeMemHesH functions create the memHes object and call its fileToMemH and memToFileH methods. The memHes class is a user defined class declared in the ./src/dpi/memhes.cpp file that performs all required actions with the memories. Let’s look at its constructor in more details. To start working with the HesDebug API either the HesDebug_connect or HesDebug_connectWait functions have to be called:

if (HesDebug_connectWait(WAIT_HES) == HES_DEBUG_ERROR) {
	std::cout << "Error: " << HesDebug_getLastError() << std::endl;
}

If the connection to the hardware failed, you can receive an error message using the HesDebug_getLastError function. The HesDebug_getLastError function is a general mechanism to report the execution errors used by all API functions. Next, the emulation is stopped with the HesDebug_stop function. Manipulation with the design can be performed while only the controlled clock is stopped:

if (HesDebug_stop() == HES_DEBUG_ERROR) { … }

Now it is possible to call various HesDebug API functions to work with a design. We will iterate through all memories available in the design, and search for the required memory by its hierarchical path. All memories in the design have a unique number starting from zero, so we will need to retrieve the total memory count in the design first:

uint32_t mem_count = HesDebug_getMemoriesCount();

We will then retrieve information about each of them using the HesDebug_memoryInfo function. This function receives pointer to the HesDebugMemoryInfo structure to return data, and a unique memory number. Next, we compare the returned instancePath with the instance path of the memory, which we want to access. If the required memory is found we store its size and depth for later usage:

HesDebugMemoryInfo mem_info;	
for(uint32_t cnt_0 = 0; cnt_0 < mem_count; cnt_0++){
	if (HesDebug_memoryInfo(cnt_0, &mem_info) == HES_DEBUG_ERROR) { …
	} else {
		if (strcmp (mem_info.instancePath, m_mem_path) == 0) {
			*status = 0;
			m_word_size = mem_info.wordSize;
			m_depth = mem_info.depth;
			…
		}
	}
}

After we’ve finished working with memories, we have to resume the emulation with the HesDebug_run command:

if (HesDebug_run(0) == HES_DEBUG_ERROR) { … }

The fileToMemH and memToFileH methods of the memHes class perform reading/writing data to memories. Let’s examine the HesDebug functions used for this. We have to stop the emulation by calling the HesDebug_stop function before actually reading or writing data. After the emulation is stopped we can use the HesDebug_memoryWrite and HesDebug_memoryRead functions to manipulate the memories. Let’s take a look at the HesDebug_memoryWrite function call in the ./src/dpi/memhes.v file:

HesDebug_memoryWrite(m_mem_path, 0, size, (HesDebugMemBuffer)data_array, 32, 0)

It accepts the following parameters:

  • path – hierarchical instance path to the memory module that is accessed.

  • address – memory address to start writing from

  • wordCount – number of words to write

  • buffer – buffer that contains values that should be written to the memory.

  • bufferWordSize – bit width of memory word

  • bufferWordShift – number of bits to right shift a word before writing into memory

The function returns true if completed successfully, otherwise it will return false.

The HesDebugMemBuffer is an array of unsigned char but in our case data_array was defined as an array of 32-bit integers:

uint32_t * data_array = new unsigned int [file_size];

This is possible because word size in our memory is equal to the integer size.

NOTE: The HesDebug_memoryRead function accepts similar parameters as the HesDebug_memoryWrite function.

After we have finished work with the memory, the emulation should be resumed with the HesDebug_run command.

Compiling and Running the Simulation

The C++ code with the DPI functions has to be compiled before using in the simulation. The Riviera-PRO ccomp command can be used to do this. You have to specify the path to the libHesDebugApi.so/dll library and the <DVM_INSTALL_DIR>/include directory so the compiler can find the HesDebug API functions:

ccomp -pli <DVM_INSTALL_DIR>/bin/libHesDebugApi.so -I <DVM_INSTALL_DIR>/include/ -sc -o <library_name> ../src/dpi/memhes.cpp

The -sc argument is required to create a library in a compatible format. After you have compiled the DPI functions, you can run the simulation. Use the -sv_lib argument of the asim command to specify the path to the library with the DPI functions:

asim … -sv_lib <library_name>

Conclusion

The HesDebug API can be used for memory initialization in the acceleration mode. In order to gain access to the API, custom DPI functions have to be created. These functions can be called in the HDL testbench providing easy and convenient methods to work with a memory modules.

Ask Us a Question
x
Ask Us a Question
x
Captcha ImageReload Captcha
Incorrect data entered.
Thank you! Your question has been submitted. Please allow 1-3 business days for someone to respond to your question.
Internal error occurred. Your question was not submitted. Please contact us using Feedback form.
We use cookies to ensure we give you the best user experience and to provide you with content we believe will be of relevance to you. If you continue to use our site, you consent to our use of cookies. A detailed overview on the use of cookies and other website information is located in our Privacy Policy.