zedboard第五课(MIO,EMIO,IRQ)

MIO是接PS侧的固定引脚,不用在XDC中约束。

EMIO是PS侧扩展到PL侧的,如果PL侧要引出到PAD上,就需要在XDC中约束。
PS侧将EMIO扩展到PL侧,是通过在BD中引出三组引脚实现的。
分别是:
tri_i_[N:0], 表示从PL侧传送给BD中的PS的信号。
tri_o[N:0], 表示从BD中的PS,传送给PL侧的信号。
tri_t[N:0],表示从BD中的PS,传送给PL侧的控制信号。
实际上,MIO也是一样有这样三组信号,只不过,MIO的这三组信号,和PS侧的MIO_IOBUF连接,经过了MIO_IOBUF模块缓冲后,变成一个唯一的信号MIO_IO,连接到了器件的PAD上。

EMIO他们没有像MIO一样,引出到PAD上,而是经过EMIO_MUX模块的转换后,直接送到了PS和PL的边界,导出给PL侧。
也就是说,如果PL侧要使用INOUT引脚。那么还需要原语的支持。IOBUF就是完成这个工作的。
IOBUF
iobuf_inst(
.IO(pin_netbias),
.I(tri_o_x),
.O(tri_i_x),
.T(tri_t_x)
);
注意,IOBUF的I脚,对应于内部的OBUF的输入端,它接收来自Fabric的一个reg驱动信号,所以,对于IOBUF而言,要把tri_o连接到I脚上去。
IOBUF的O脚,对应于内部的IBUF的输出端,IBUF把从PAD上接收的信号缓冲输出,用来驱动Fabric的lut和reg,所以,对于EMIO而言,要把tri_i连接到O脚上去。
IOBUF的T脚,对应于内部OBUF的EN端,它接收来自Fabric的一个reg驱动信号,所以,对于IOBUF而言,要把tri_t连接到T脚上去。

BANK0, GPIO[31:0],MIO专属GPIONUM。
BANK1,GPIO[53:32],MIO专属GPIONUM。
BANK2,GPIO[85:54],EMIO专属的GPIONUM,在PL侧显示为EMIO[31:0]。
BANK3,GPIO[117:86],EMIO专属的GPIONUM,在PL侧显示为EMIO[63:32]。

我们知道,在standaloneOS里,对GPIO模块的编程,架构是基于SOD设计思想的。
我们在使用GPIO时,首先要基于SOD设计思想,形成模型的概念。
我们知道,在standalone中使用GPIO时,有两个重要的结构体。

typedef struct {
	XGpioPs_Config GpioConfig;	/**< Device configuration */
	u32 IsReady;			/**< Device is initialized and ready */
	XGpioPs_Handler Handler;	/**< Status handlers for all banks */
	void *CallBackRef; 		/**< Callback ref for bank handlers */
	u32 Platform;			/**< Platform data */
	u32 MaxPinNum;			/**< Max pins in the GPIO device */
	u8 MaxBanks;			/**< Max banks in a GPIO device */
} XGpioPs;

typedef struct {
	u16 DeviceId;		/**< Unique ID of device */
	u32 BaseAddr;		/**< Register base address */
} XGpioPs_Config;

typedef void (*XGpioPs_Handler) (void *CallBackRef, u32 Bank, u32 Status);

他们都是在xgpiops.h这个文件中定义的。所有的操作函数,都是以这两个结构体为参数进行的。
XGpioPs定义了一个Resource控制块。(RCB)它包含了控制一个资源,所需要的全部信息,包括数据,状态,配置等等。
XGpioPs_Config定义了一个Resource描述块。(RDB)它包含了一个资源的硬件配置信息。
显然,从逻辑上看,RCB包含一个RDB。或者说。RCB内嵌一个RDB。

SOD设计思想下,结构体之间的关系有很多种,最常见的就是内嵌和关联,何时设计成内嵌,何时设计成关联,是SOD设计思想的精髓所在。
主要取决于结构体之间的逻辑关系,如果两个结构体,在逻辑上是相互独立的,由于在进行操作时,需要二者协同,这个时候,可以将两个结构体设计成关联形式的耦合。
如果两个结构体,在逻辑上是包含关系,小结构体是大结构体的一个子部分,那么就要设计成内嵌。
如果两个结构体,在逻辑上是衍生关系,大结构体是小结构体的更具体的衍生,那么也要设计成内嵌。

在SOD设计思想的框架下,任何操作函数,要对资源进行操作,只能通过RCB来进行。通过RCB来查找所需的信息,甚至通过RCB来查找所需的操作函数的指针。
同时,操作函数的参数与返回值的设计,也要遵循SOD设计思想。何时使用传址方式,何时使用传值方式,都要符合SOD设计思想。
我们知道,函数在调用时,才会分配栈帧(stackframe),参数和返回值,都只在栈帧中存在,一旦函数调用结束,栈帧就被销毁了。所以,理论上,传址比传值的生存期更长,即使栈帧中的指针被销毁,在栈帧之外,一定还有别的地方,存放着指针,可以继续使用函数调用时曾经通过指针来使用过的变量,但是传值则不同,栈帧销毁后,别的地方再也无法使用栈帧中的变量了。
基于栈帧的生存期的考虑,原则上,凡是系统内的实体对象,都要使用传址方式,只有那些用过即毁的数据,才能使用传值方式。
对于使用传址方式的参数,我们通常称之为句柄(Handle),而对于使用传值方式的参数,我们通常称之为数据条目(DataEntry)或者数据向量(DataVector)。一个DataVector,是整个参数表中所有的传值方式的参数所组成的一个向量。这样分类的好处在于,很多场合下,我们可以从数据库的Table中查找到数据条目,并利用条目来完成业务要求的任务。
将参数分类成Handle和DataVector之后,更利于我们对操作函数的设计,我们可以聚焦到函数的业务任务的划分,它需要控制谁,有什么约束要求。
函数的RetVal,通常用来返回函数的操作状态(Status),我们知道,RetVal是被调用函数和调用者之间的一个单向通信手段,而且只在最后交接的时机是有效的。所以,最好的方式,就是让被调用函数向调用者反馈操作状态。可以理解为一种Report机制或者ACK机制。
如果让函数RetVal,携带数据返回,这也是一种常见的通信机制,它相当于复用了状态通信的通道,利用状态通道传输数据。虽然不是推荐的数据通信方式,但是有的时候,可以使程序设计更简洁,更易懂。
例如,让RetVal返回一个指针,指针是NULL或者NOT_NULL的,这本身就携带了状态。又例如,让RetVal返回一个Result,Result的编码规则,决定Result的值的含义,是属于VALID_Data还是INVALID_Data,或者是VALID_STATUS的某一个STATUS_CODE。可以看出,能够复用状态通道的前提条件是,RetVal不会有歧义。状态码不能和有效数据发生重叠。这样,调用者将无法分辨出到底是状态码还是有效数据。
但是无论如何,这都不是推荐的程序设计方式。
如果是需要让被调用函数修改它外部的数据对象,最好的方式是在参数表里,给它一个句柄。RetVal就专门做状态反馈。

如果是资源控制块,那么它必定是系统实体对象,所以我们都是设计成传址方式。而在我们每次操作GPIO时,所需要指定的PINNUM,BANKNUM等,可以设计成数据向量,所以他们是设计成传值方式的。

回到GPIO的模块,我们继续分析
有几个重要的操作函数

XGpioPs_Config *XGpioPs_LookupConfig(u16 DeviceId);

它通过查找系统表(SystemTable)来获得RDB的GRP。
注意,SystemTable中的资源,是全局数据,所以,对于这些全局数据的指针,我们称为全局资源指针(Global Resource Pointer),简称GRP。而这个函数中使用的DeviceID,则称为系统表索引号,(System Table Index),简称STI。基于SOD设计思想的程序架构中,通常会在系统表中注册多个条目,每个条目都有自己的STI,通过Lookup操作,来获得相应的资源句柄,也就是GRP。

s32 XGpioPs_CfgInitialize(XGpioPs *InstancePtr, XGpioPs_Config *ConfigPtr,
			   u32 EffectiveAddr);

它对XGPIOPS进行初始化,所使用的参考GRP,就是之前提到的,Lookup找到的GRP。

/* Pin APIs in xgpiops.c */
u32 XGpioPs_ReadPin(XGpioPs *InstancePtr, u32 Pin);
void XGpioPs_WritePin(XGpioPs *InstancePtr, u32 Pin, u32 Data);
void XGpioPs_SetDirectionPin(XGpioPs *InstancePtr, u32 Pin, u32 Direction);
u32 XGpioPs_GetDirectionPin(XGpioPs *InstancePtr, u32 Pin);
void XGpioPs_SetOutputEnablePin(XGpioPs *InstancePtr, u32 Pin, u32 OpEnable);
u32 XGpioPs_GetOutputEnablePin(XGpioPs *InstancePtr, u32 Pin);

这些函数用来对GPIO进行控制。实质上,这些函数是经过二次封装的,AXI对GPIO的操作,是以BANK为单位进行的,所以,对单个PIN的操作,其实是找到相应的BANK,然后生成MASK,并利用MASK来实现的。

/* Pin APIs in xgpiops_intr.c */
void XGpioPs_SetIntrTypePin(XGpioPs *InstancePtr, u32 Pin, u8 IrqType);
u8 XGpioPs_GetIntrTypePin(XGpioPs *InstancePtr, u32 Pin);

void XGpioPs_IntrEnablePin(XGpioPs *InstancePtr, u32 Pin);
void XGpioPs_IntrDisablePin(XGpioPs *InstancePtr, u32 Pin);
u32 XGpioPs_IntrGetEnabledPin(XGpioPs *InstancePtr, u32 Pin);
u32 XGpioPs_IntrGetStatusPin(XGpioPs *InstancePtr, u32 Pin);
void XGpioPs_IntrClearPin(XGpioPs *InstancePtr, u32 Pin);

VIVADO SDK中,提供的BSP包,提供了操作GPIO的库函数,自然也包含了GPIO_INTR的库函数。提供了包括中断源配置,中断标志读取,中断位清除等操作。
以上函数,是用来对GPIO的INTERRUPT功能进行操作的。

对中断系统的寄存器进行配置,可以配置GPIO中断源,包括对哪些GPIO进行中断使能,中断监测,监测哪些中断类型,究竟是电平触发还是边沿触发,等等。
PS侧的IO中断,会产生一个IRQ中断,118个GPIO ,统一生成一个IRQ52。即IRQ号码为#52的一个中断请求。至于究竟是哪个GPIO产生的中断,产生的是哪种类型的中断,则再在SUB_IRQ中进行详细判断。

如前所述,MIO和EMIO均用作GPIO中断源。但是MIO接到FIXIO中,无法和PL交互。所以,如果PL侧要向PS侧传递一个GPIO中断源,则需要利用EMIO进行。
EMIO既可以接到PL侧的PAD上,接收来自外设的中断输入,也可以不接到PAD,只是接到PL侧的逻辑模块中,接收来自逻辑模块的中断输入。

GIC控制器的使用,standalone里的架构,也是仿照linux的风格来实现的。
在BSP里,提供了GIC的库函数,提供了对GIC的各种操作函数,以及封装了对GIC操作所需的数据对象。
中断的执行是基于Callback的,ISR只负责接收中断,做预处理,通过查找对应的数据对象,找到CallbackHandler,然后跳转到CallbackHandler中执行事务处理代码。
所以,在应用中,需要做两个事情。一,编写CallbackHandler,二,setCallbackHandler。
需要注意的是,由于中断是全局的,所以配置的中断描述数据对象,必须是全局的,用static限定。如果是某个栈变量,那么在函数返回时,就被销毁了。此时,CallbackRef就是一个无效的数据对象。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值