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.
// 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;
};