如果使用C++来设计程序,通常使用empty project。
这就需要对整个代码架构很熟悉。
首先就是要熟悉BSP代码架构,
BSP中,提供了各种模块,这些模块都是SOD风格设计的。
在H文件中,设计typedef的各种struct,union,并声明操作函数API,这些API都是SOD风格的,所以可以看到,首参都是struct object的指针。
BSP提供的H文件,进行了编译唯一性保护,使用了保护宏,
为了能够支持CPP中使用C的代码,使用了外部C声明,
例如:
#ifndef XIL_ASSERT_H /* prevent circular inclusions */
#define XIL_ASSERT_H /* by using protection macros */
。。。
#ifdef __cplusplus
extern "C" {
#endif
。。。
#ifdef __cplusplus
}
#endif
#endif /* end of protection macro */
BSP包中的每个模块,都在MACHINE/libsrc/xxx_module文件夹下,存放对应的H文件和C文件,
为了能够让APP工程能够以统一的方式include各个H文件,BSP工程中,又将各个模块中的H文件,拷贝了一份,放到MACHINE/include/文件夹中。
所以,当我们需要使用BSP包中的某个模块时,只需要在xxx_module文件夹下,找到对应的H文件,就知道include下的文件的具体名称了。
例如:
#include "xauto_focus.h"
BSP包主要是提供基本读写的驱动代码。以Struct object的形式,向APP提供硬件操作能力,表现为以struct object对核心,发起读写操作。
为了使用户不用关心具体的硬件相关信息,BSP提供了一系列API,用以实现具体的读写某个特定地址的REG的功能,这些API均以GET_SET进行前缀命名,用以表明这是用于数据层面的。
例如:
//xauto_focus.c
u32 XAuto_focus_Get_roi_x(XAuto_focus *InstancePtr) {
u32 Data;
Data = XAuto_focus_ReadReg(InstancePtr->Control_bus_BaseAddress, XAUTO_FOCUS_CONTROL_BUS_ADDR_ROI_X_DATA);
return Data;
}
void XAuto_focus_Set_roi_x(XAuto_focus *InstancePtr, u32 Data) {
XAuto_focus_WriteReg(InstancePtr->Control_bus_BaseAddress, XAUTO_FOCUS_CONTROL_BUS_ADDR_ROI_X_DATA, Data);
}
而寄存器的读写操作,则是用宏拟函数来实现的,并且是以READ_WRITE进行命名,用以表明这是用于硬件操作层面的。
例如:
//xauto_focus.h
#define XAuto_focus_ReadReg(BaseAddress, RegOffset) \
Xil_In32((BaseAddress) + (RegOffset))
#define XAuto_focus_WriteReg(BaseAddress, RegOffset, Data) \
Xil_Out32((BaseAddress) + (RegOffset), (u32)(Data))
这个宏拟函数,是对底层IO函数的一次封装,底层IO函数,是以IN_OUT进行命名的,用以表明这是总线IO层面的。
例如:
//xil_io.h
static INLINE u32 Xil_In32(UINTPTR Addr)
{
return *(volatile u32 *) Addr;
}
static INLINE void Xil_Out32(UINTPTR Addr, u32 Value)
{
#ifndef ENABLE_SAFETY
volatile u32 *LocalAddr = (volatile u32 *)Addr;
*LocalAddr = Value;
#else
XStl_RegUpdate(Addr, Value);
#endif
}
为了能够使用某个硬件模块,就必须能够正确识别该硬件模块对应的REG所在的区域地址,并进行读写。这在BSP里,是以SOD风格进行设计的。将硬件模块对应的REG区域,base+offset,将base封装到struct中,而将offset封装到某个具体的get_set的API中。
这就需要能够正确的初始化struct,也就是填充其中的base。对应的API是xxx_Initialize。
这在BSP中,是一个通用的套路。在xxx_Initialize中,调用lookup查找对应的xparameters,然后调用xxx_cfgInitialize,对struct object进行初始化,填充base。
例如:
//xauto_focus_sinit.c
int XAuto_focus_Initialize(XAuto_focus *InstancePtr, u16 DeviceId) {
XAuto_focus_Config *ConfigPtr;
ConfigPtr = XAuto_focus_LookupConfig(DeviceId);
return XAuto_focus_CfgInitialize(InstancePtr, ConfigPtr);
}
XAuto_focus_Config *XAuto_focus_LookupConfig(u16 DeviceId) {
XAuto_focus_Config *ConfigPtr = NULL;
int Index;
for (Index = 0; Index < XPAR_XAUTO_FOCUS_NUM_INSTANCES; Index++) {
if (XAuto_focus_ConfigTable[Index].DeviceId == DeviceId) {
ConfigPtr = &XAuto_focus_ConfigTable[Index];
break;
}
}
return ConfigPtr;
}
//xauto_focus.c
int XAuto_focus_CfgInitialize(XAuto_focus *InstancePtr, XAuto_focus_Config *ConfigPtr) {
InstancePtr->Control_bus_BaseAddress = ConfigPtr->Control_bus_BaseAddress;
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
return XST_SUCCESS;
}
//xauto_focus_g.c
XAuto_focus_Config XAuto_focus_ConfigTable[XPAR_XAUTO_FOCUS_NUM_INSTANCES] =
{
{
XPAR_VIDEO_PROCESS_AUTO_FOCUS_0_DEVICE_ID,
XPAR_VIDEO_PROCESS_AUTO_FOCUS_0_S_AXI_CONTROL_BUS_BASEADDR
}
};
如果想要了解具体的硬件相关的细节信息。可以查看几个相关的H文件。
例如:
//xauto_focus_hw.h
// CONTROL_BUS
// 0x00 : Control signals
// bit 0 - ap_start (Read/Write/COH)
// bit 1 - ap_done (Read/COR)
// bit 2 - ap_idle (Read)
// bit 3 - ap_ready (Read)
// bit 7 - auto_restart (Read/Write)
// others - reserved
// 0x04 : Global Interrupt Enable Register
// bit 0 - Global Interrupt Enable (Read/Write)
// others - reserved
#define XAUTO_FOCUS_CONTROL_BUS_ADDR_AP_CTRL 0x00
#define XAUTO_FOCUS_CONTROL_BUS_ADDR_GIE 0x04
这个文件中,列出的是硬件模块的REG的offset。
//xparameters.h
/******************************************************************/
/* Definitions for driver AUTO_FOCUS */
#define XPAR_XAUTO_FOCUS_NUM_INSTANCES 1
/* Definitions for peripheral VIDEO_PROCESS_AUTO_FOCUS_0 */
#define XPAR_VIDEO_PROCESS_AUTO_FOCUS_0_DEVICE_ID 0
#define XPAR_VIDEO_PROCESS_AUTO_FOCUS_0_S_AXI_CONTROL_BUS_BASEADDR 0x44A90000
#define XPAR_VIDEO_PROCESS_AUTO_FOCUS_0_S_AXI_CONTROL_BUS_HIGHADDR 0x44A9FFFF
这个文件中,列出的是硬件模块的ID和硬件的REG的base。
有些硬件模块,启动了interrupt,所以还会有xxx_intr.c这个文件。例如
xgpio_intr.c
这个文件中,提供了interrupt相关的REG的读写操作。
void XGpio_InterruptGlobalEnable(XGpio *InstancePtr)
{
XGpio_WriteReg(InstancePtr->BaseAddress, XGPIO_GIE_OFFSET,
XGPIO_GIE_GINTR_ENABLE_MASK);
}
void XGpio_InterruptGlobalDisable(XGpio *InstancePtr)
{
XGpio_WriteReg(InstancePtr->BaseAddress, XGPIO_GIE_OFFSET, 0x0);
}
void XGpio_InterruptEnable(XGpio *InstancePtr, u32 Mask)
{
u32 Register;
Register = XGpio_ReadReg(InstancePtr->BaseAddress, XGPIO_IER_OFFSET);
XGpio_WriteReg(InstancePtr->BaseAddress, XGPIO_IER_OFFSET,
Register | Mask);
}
void XGpio_InterruptDisable(XGpio *InstancePtr, u32 Mask)
{
u32 Register;
Register = XGpio_ReadReg(InstancePtr->BaseAddress, XGPIO_IER_OFFSET);
XGpio_WriteReg(InstancePtr->BaseAddress, XGPIO_IER_OFFSET,
Register & (~Mask));
}
void XGpio_InterruptClear(XGpio * InstancePtr, u32 Mask)
{
u32 Register;
Register = XGpio_ReadReg(InstancePtr->BaseAddress, XGPIO_ISR_OFFSET);
XGpio_WriteReg(InstancePtr->BaseAddress, XGPIO_ISR_OFFSET,
Register & Mask);
}
u32 XGpio_InterruptGetEnabled(XGpio * InstancePtr)
{
return XGpio_ReadReg(InstancePtr->BaseAddress, XGPIO_IER_OFFSET);
}
u32 XGpio_InterruptGetStatus(XGpio * InstancePtr)
{
return XGpio_ReadReg(InstancePtr->BaseAddress, XGPIO_ISR_OFFSET);
}
从中可以看出,这些API,都是以XGPIO这个struct object为核心对象,读写相关的REG完成的。
如果需要使用interrupt,那么还需要使用INTC这个模块,也就是GIC,
例如
xintc.c
如果使用GIC,那么需要将GIC挂载到exception,这个standaloneOS的常规处理方式。
例如
xil_exception.c
typedef struct {
Xil_ExceptionHandler Handler;
void *CallBackRef;
} MB_ExceptionVectorTableEntry;
void Xil_ExceptionEnable(void)
{
#ifdef MICROBLAZE_EXCEPTIONS_ENABLED
microblaze_enable_exceptions();
#endif
microblaze_enable_interrupts();
}
void Xil_ExceptionDisable(void)
{
#ifdef MICROBLAZE_EXCEPTIONS_ENABLED
microblaze_disable_exceptions();
#endif
microblaze_disable_interrupts();
}
void Xil_ExceptionRegisterHandler(u32 Id, Xil_ExceptionHandler Handler,
void *Data)
{
if (Id == XIL_EXCEPTION_ID_INT) {
MB_InterruptVectorTable[0].Handler = Handler;
MB_InterruptVectorTable[0].CallBackRef = Data;
}
else {
#ifdef MICROBLAZE_EXCEPTIONS_ENABLED
MB_ExceptionVectorTable[Id].Handler = Handler;
MB_ExceptionVectorTable[Id].CallBackRef = Data;
#endif
}
}
typedef void (*Xil_ExceptionHandler)(void *Data);
typedef void (*XInterruptHandler) (void *InstancePtr);
可以看出,
如果使用了Exception,那么就注册到MB_ExceptionVectorTable这个全局表中。
或者注册到MB_InterruptVectorTable这个全局表中。
// microblaze_interrupts_i.h
typedef struct
{
XInterruptHandler Handler;
void *CallBackRef;
} MB_InterruptVectorTableEntry;
// microblaze_interrupts_g.c
MB_InterruptVectorTableEntry MB_InterruptVectorTable[] =
{
{ XIntc_DeviceInterruptHandler,
(void*) XPAR_MICROBLAZE_SUBSYSTEM_MICROBLAZE_0_AXI_INTC_DEVICE_ID}
};
可以看出,全局表中,已经注册了一个Handler,来看看这个handler是什么,
XIntc_DeviceInterruptHandler,位于xintc_l.c中,
void XIntc_DeviceInterruptHandler(void *DeviceId)
{
u32 IntrStatus;
u32 IntrMask = 1;
int IntrNumber;
XIntc_Config *CfgPtr;
u32 Imr;
/* Get the configuration data using the device ID */
CfgPtr = &XIntc_ConfigTable[(u32)DeviceId];
/* Get the interrupts that are waiting to be serviced */
IntrStatus = XIntc_GetIntrStatus(CfgPtr->BaseAddress);
/* Service each interrupt that is active and enabled by
* checking each bit in the register from LSB to MSB which
* corresponds to an interrupt input signal
*/
for (IntrNumber = 0; IntrNumber < CfgPtr->NumberofIntrs;
IntrNumber++) {
...
TablePtr = &(CfgPtr->HandlerTable[IntrNumber]);
TablePtr->Handler(TablePtr->CallBackRef);
...
}
}
}
这个函数用来遍历XINTC_CONFIG中注册的Handler Table,将所有的Handler调用一次。
看看看xintc.h中的定义
typedef struct {
u16 DeviceId; /**< Unique ID of device */
UINTPTR BaseAddress; /**< Register base address */
u32 AckBeforeService; /**< Ack location per interrupt */
int FastIntr; /**< Fast Interrupt enabled */
u32 IntVectorAddr; /**< Interrupt Vector Address */
int NumberofIntrs; /**< Number of Interrupt sources */
u8 VectorAddrWidth; /**< Width of vector address */
u32 Options; /**< Device options */
int IntcType; /**< Intc type 0 - No Cascade Mode
1 - primary instance
2 - secondary instance
3 - last instance */
/** Static vector table of interrupt handlers */
#if XPAR_INTC_0_INTC_TYPE != XIN_INTC_NOCASCADE
XIntc_VectorTableEntry HandlerTable[XIN_CONTROLLER_MAX_INTRS];
#else
XIntc_VectorTableEntry HandlerTable[XPAR_INTC_MAX_NUM_INTR_INPUTS];
#endif
} XIntc_Config;
typedef struct {
UINTPTR BaseAddress; /**< Base address of registers */
u32 IsReady; /**< Device is initialized and ready */
u32 IsStarted; /**< Device has been started */
u32 UnhandledInterrupts; /**< Intc Statistics */
XIntc_Config *CfgPtr; /**< Pointer to instance config entry */
} XIntc;
可以看到,Xintc结构体中,关联到一个Xintc_Config结构体。
而在xintc_g.c中,是一个全局表,里面在0号位置,分配了一个Xintc_Config结构体。这就是调用lookup时,返回的结构体。
XIntc_Config XIntc_ConfigTable[] =
{
{
XPAR_MICROBLAZE_SUBSYSTEM_MICROBLAZE_0_AXI_INTC_DEVICE_ID,
XPAR_MICROBLAZE_SUBSYSTEM_MICROBLAZE_0_AXI_INTC_BASEADDR,
XPAR_MICROBLAZE_SUBSYSTEM_MICROBLAZE_0_AXI_INTC_KIND_OF_INTR,
XPAR_MICROBLAZE_SUBSYSTEM_MICROBLAZE_0_AXI_INTC_HAS_FAST,
XPAR_MICROBLAZE_SUBSYSTEM_MICROBLAZE_0_AXI_INTC_IVAR_RESET_VALUE,
XPAR_MICROBLAZE_SUBSYSTEM_MICROBLAZE_0_AXI_INTC_NUM_INTR_INPUTS,
XPAR_MICROBLAZE_SUBSYSTEM_MICROBLAZE_0_AXI_INTC_ADDR_WIDTH,
XIN_SVC_SGL_ISR_OPTION,
XPAR_MICROBLAZE_SUBSYSTEM_MICROBLAZE_0_AXI_INTC_TYPE,
{
{
XNullHandler,
(void *) XNULL
},
...
}
}
};
来看看xintc.c文件
int XIntc_Initialize(XIntc * InstancePtr, u16 DeviceId)
{
u8 Id;
XIntc_Config *CfgPtr;
u32 NextBitMask = 1;
CfgPtr = XIntc_LookupConfig(DeviceId);
/*
* Set some default values
*/
InstancePtr->IsReady = 0;
InstancePtr->IsStarted = 0; /* not started */
InstancePtr->CfgPtr = CfgPtr;
InstancePtr->CfgPtr->Options = XIN_SVC_SGL_ISR_OPTION;
InstancePtr->CfgPtr->IntcType = CfgPtr->IntcType;
InstancePtr->BaseAddress = CfgPtr->BaseAddress;
for (Id = 0; Id < CfgPtr->NumberofIntrs; Id++) {
if ((InstancePtr->CfgPtr->HandlerTable[Id].Handler == 0) ||
(InstancePtr->CfgPtr->HandlerTable[Id].Handler ==
XNullHandler)) {
InstancePtr->CfgPtr->HandlerTable[Id].Handler =
StubHandler;
}
InstancePtr->CfgPtr->HandlerTable[Id].CallBackRef = InstancePtr;
}
...
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
return XST_SUCCESS;
}
这个函数对结构体进行填充,完成初始化。
int XIntc_Connect(XIntc * InstancePtr, u8 Id,
XInterruptHandler Handler, void *CallBackRef)
{
XIntc_Config *CfgPtr;
InstancePtr->CfgPtr->HandlerTable[Id].Handler = Handler;
InstancePtr->CfgPtr->HandlerTable[Id].CallBackRef =
CallBackRef;
return XST_SUCCESS;
}
这个函数完成connect,也就是将Handler和CallbackRef填充到Xintc_Config的HandlerTable中去。
void XIntc_Enable(XIntc * InstancePtr, u8 Id)
{
u32 CurrentIER;
u32 Mask;
XIntc_Config *CfgPtr;
Mask = XIntc_BitPosMask[Id];
CurrentIER = XIntc_In32(InstancePtr->BaseAddress +
XIN_IER_OFFSET);
XIntc_Out32(InstancePtr->BaseAddress + XIN_IER_OFFSET,
(CurrentIER | Mask));
}
void XIntc_Disable(XIntc * InstancePtr, u8 Id)
{
u32 CurrentIER;
u32 Mask;
XIntc_Config *CfgPtr;
Mask = XIntc_BitPosMask[Id];
CurrentIER = XIntc_In32(InstancePtr->BaseAddress +
XIN_IER_OFFSET);
XIntc_Out32(InstancePtr->BaseAddress + XIN_IER_OFFSET,
(CurrentIER & ~Mask));
}
Enable函数以及Disable函数通过读写REG,使能XINTC。
int XIntc_Start(XIntc * InstancePtr, u8 Mode)
{
u32 MasterEnable = XIN_INT_MASTER_ENABLE_MASK;
XIntc_Config *CfgPtr;
int Index;
MasterEnable |= XIN_INT_HARDWARE_ENABLE_MASK;
InstancePtr->IsStarted = XIL_COMPONENT_IS_STARTED;
/* Start the master */
XIntc_Out32(InstancePtr->BaseAddress + XIN_MER_OFFSET, MasterEnable);
return XST_SUCCESS;
}
START函数通过读写REG,启动INTC。
在D:\Xilinx\SDK\2019.1\data\embeddedsw\XilinxProcessorIPLib\drivers\intc_v3_9\examples下,
找到xintc_example.c文件。
int IntcExample(u16 DeviceId)
{
int Status;
Status = XIntc_Initialize(&InterruptController, DeviceId);
Status = SetUpInterruptSystem(&InterruptController);
while (1)
{
if (InterruptProcessed) {
break;
}
}
return XST_SUCCESS;
}
可以看到,需要两个阶段进行INTC的配置,
首先是初始化,然后是配置。
int SetUpInterruptSystem(XIntc *XIntcInstancePtr)
{
int Status;
Status = XIntc_Connect(XIntcInstancePtr, XPAR_INTC_0_UARTLITE_0_VEC_ID,
(XInterruptHandler)DeviceDriverHandler,
(void *)0);
// add more connect handler here
...
Status = XIntc_Start(XIntcInstancePtr, XIN_REAL_MODE);
XIntc_Enable(XIntcInstancePtr, INTC_DEVICE_INT_ID);
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XIntc_InterruptHandler,
XIntcInstancePtr);
// add more register exception handler here
...
Xil_ExceptionEnable();
return XST_SUCCESS;
}
void DeviceDriverHandler(void *CallbackRef)
{
InterruptProcessed = TRUE;
return;
}
配置过程如下(rear to front):
首先,CONNECT,将事先编写的callback函数,和需要的callbackref,挂载到XINC中对应的VEC_ID号的entry中。
然后,start,启动XINTC,注意设置正确的MODE,
然后,enable,使能INTC模块的硬件功能,
然后,对exception进行处理,
首先,初始化exception,
然后,注册exception的handler,如前所述,由于给的ID是XIL_EXCEPTION_ID_INT,所以注册到MB_InterruptVectorTable中的0号位置。如前所述,这个位置初始注册的是XIntc_DeviceInterruptHandler,现在替换成XIntc_InterruptHandler,并配置了callbackref,是xintc结构体的指针。
void XIntc_InterruptHandler(XIntc * InstancePtr)
{
// add user logic here
XIntc_DeviceInterruptHandler((void *)
((u32) (InstancePtr->CfgPtr->DeviceId)));
// add user logic here
}
这个函数内部也是调用了XIntc_DeviceInterruptHandler,只是封装了一次。
我们可以在这个封装函数中,添加我们自己的HOOK代码。
最后,enable,使能exception。