AN 947: Testing the Nios® II Software with Unit-Test Framework

ID 683606
Date 4/30/2021
Public

Intrinsic Function Overwrite

Nios® II software operates on top of the board support package (BSP) for the Nios® II processor. BSP provides many useful application programming interfaces (APIs) which assist software to execute on the Nios® II processor and interact with hardware. For Nios® II software, the hardware interaction means reading from or writing to certain registers in a certain way. Therefore, the System Mock can intercept these read or write (RW) operations to model the Nios® II software interactions with hardware.

In the Nios® II BSP, there are macros defined for register RW. The commonly used ones are IORD, IORD_32DIRECT, IOWR, and IOWR_32DIRECT. These macros call built-in functions __builtin_ldwio and __builtin_stwio. System Mock can provide new definitions for these built-in functions. Overwrite the definitions of these built-in functions to redirect the memory access and augment it with side effects.

One caveat of this technique is that developers must write code in a certain way. All Avalon® memory-mapped interface RW operations must use function calls (for example: IORD/IOWR). Memory access through pointers is allowed in this unit-test framework. The prerequisite is that the System Mock has allocated a memory block and replaced the base address in Nios® II code appropriately. However, System Mock cannot intercept or augment these pointer-based memory access.

Example code for intrinsic function overwrite:
// NIOS II software code
// main.c
alt_u32 mb_val = IORD_32DIRECT(U_MAILBOX_AVMM_BRIDGE_BASE, 0);

// NIOS II BSP provides macros for generic read and write
// HAL/inc/io.h
#define __IO_CALC_ADDRESS_DYNAMIC(BASE, OFFSET) \
  ((void *)(((alt_u8*)BASE) + (OFFSET)))

#define IORD_32DIRECT(BASE, OFFSET) \
  __builtin_ldwio (__IO_CALC_ADDRESS_DYNAMIC ((BASE), (OFFSET)))

#define IOWR_32DIRECT(BASE, OFFSET, DATA) \
  __builtin_stwio (__IO_CALC_ADDRESS_DYNAMIC ((BASE), (OFFSET)), (DATA))

// In unit-test, System Mock framework provides the definitions of these built-in functions
// bsp_mock.h
static alt_u32 __builtin_ldwio(void* src)
{
    return SYSTEM_MOCK::get()->get_mem_word(src);
}

static void __builtin_stwio(void* dst, alt_u32 data)
{
    return SYSTEM_MOCK::get()->set_mem_word(dst, data);
}

// System Mock goes through the list of IP Mock modules and passes on the Read/Write requests.
// system_mock.cpp
alt_u32 SYSTEM_MOCK::get_mem_word(void* addr)
{
    alt_u32 ret = 0;
 
    // Dispatch to the appropriate handler based on the address
    for (auto& mock : m_memory_mocks)
    {
        if (mock->is_addr_in_range(addr))
        {
            ret = mock->get_mem_word(addr);
            break;
        }
    }

    return ret;
}

void SYSTEM_MOCK::set_mem_word(void* addr, alt_u32 data)
{
    // skipped ... 
}

// In Mailbox IP mock module, define IP behavior in get/set_mem_word functions
// mailbox_mock.h
class MAILBOX_MOCK : public MEMORY_MOCK_IF
{
public:
    MAILBOX_MOCK();
    virtual ~MAILBOX_MOCK();
 
    void reset() override;
    alt_u32 get_mem_word(void* addr) override;
    void set_mem_word(void* addr, alt_u32 data) override;
    bool is_addr_in_range(void* addr) override;
 
private:
    // A simple array to store register values in Mailbox
    std::array<alt_u32, U_MAILBOX_AVMM_BRIDGE_SPAN> m_mailbox_regs;
};
Consider a simple FPGA design with a Nios® II system and a mailbox on an Avalon® memory-mapped interface. Here, a mailbox is a simple collection of hardware registers that allows Intel FPGA Nios® II processor to communicate with the outside world. These mailbox registers reside in 0x2000–0x2fff of the Avalon® memory-mapped interface address range. When the Nios® II software executes in an x86 unit-test environment, there are RW operations on this address range, which is arbitrary and probably illegal for CPU processes. To avoid that, in System Mock, user can define a 4KB array representing the mailbox. System Mock overwrites the intrinsic functions and redirects the memory access to this array.
Figure 1. Intrinsic Function Overwrite Example