从SOD到OOD(VIVADO SDK 中BSP包驱动代码)

如果使用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。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,OOA和OOD是软件开发的两个重要阶段,OOA主要是对需求进行分析和抽象,而OOD则是在OOA的基础上进行具体的设计和实现。类图是OOA和OOD常用的一种图形化工具,用于表示系统的类、属性、方法等信息,下面分别介绍OOA类图和OOD类图。 OOA类图: OOA类图主要用于表示系统的概念和对象之间的关系,它是从用户需求出发,对系统进行分析和抽象的产物。OOA类图的类通常是从用户需求抽象出来的,它们具有一些共同的属性和方法,用于描述系统的概念和对象之间的关系。OOA类图的类通常不含具体的实现细节,而是强调系统的概念和对象之间的关系。 OOD类图: OOD类图主要用于表示系统的具体实现,它是在OOA类图的基础上进行具体的设计和实现的产物。OOD类图的类通常含具体的实现细节,它们具有一些具体的属性和方法,用于描述系统的具体实现。OOD类图的类通常是从OOA类图的类演化而来的,它们保留了OOA类图的概念和对象之间的关系,同时增加了具体的实现细节。 下面是一个简单的OOA类图和OOD类图的示例: ```uml @startuml class Person { -name: string -age: int +getName(): string +setName(name: string): void +getAge(): int +setAge(age: int): void } class Student { -id: string -major: string +getId(): string +setId(id: string): void +getMajor(): string +setMajor(major: string): void } Person <|-- Student @enduml ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值