了解主要功能【思考如何触发】->看管脚->看寄存器->围绕寄存器列出测试点->搭建tb开始测试。

1.1 理解AHB_GPIO

1.1.1 介绍

GPIO的概念

  • GPIO就是一个通用的资源IO管理的模块,它的作用就是将数据送到有限的管脚上面,用它来管理外部的信号与我们芯片内部信号之间的交互,避免了资源的浪费。
  • AHB_GPIO提供了一个16bit的IO接口(数据既可以进来也可以出去【芯片与芯片外部的环境之间通过I/O Pad进行沟通】); :::info 【补充】什么时候需要避免资源的浪费?
  1. 管脚资源很少的时候;
  2. 内部的资源相当多,已经大于管脚所能接受的数量的时候 ::: 作用

  3. 能产生一个中断,通过中断信号线的拉高和拉低来通知其他的模块,中断的作用:

    1. 可能会送给处理器;
    2. 可能会送给其他的IO Controller;
  4. 提供bit的掩位;
  5. 此外还有一个附属的功能就是提供PIN_MUX,Pin_Mux的作用也是为了有效的利用管脚的资源,原因是大多数情况下GPIO所能管理的管脚是有限的,我们将GPIO做成这样的一个小模块,可以例化多次来管理更多的管脚,所以采用Pin_Mux来进行选择;

    比如在我们的PORTFUNC[15:0]为0的时候,会将PORTOUT信号线和PORTEN信号线送入到下一级。 在后续测试的过程中我们可能会将TB建立成不同的形态:

    1. 不考虑整个GPIO模块的Pin_Mux功能,将它当做一个简单的模块,那么我们只需要给定一些AHB的信号,类似于AHB_RAM那个项目中进行验证即可,在系统一级中在验证Pin_Mux的功能;
    2. 考虑GPIO模块的功能,我们将它作为选择器的功能也考虑进来进行验证,在模块一级就完成验证,这样避免了在系统一级的错误。
  6. 提供控制寄存器的set和clear功能来进行一些安全的线程操作;

  7. INPUT有可能是一个异步的信号,我们需要对它进行一个异步的驱动,来自外部的这个信号和GPIO这个芯片内部的信号一定是不同步的。

image.png
管脚信号
image.png

如果Alternative_function_default默认被设置成了16’h0000,那么所有的信号都是从GPIO内部的寄存器输出的; 如果该参数被设置成其他信号,那么这些信号就是从外部的Alternate function signals采集进来的;

1.1.2 功能及特点

INT相关的逻辑

:::info 对于interrupt,我们还是主要理解:
interrupt信号到底是怎么产生的?是由于监测到的portin信号触发了某一个信号产生的吗?还是由于对寄存器中的int信号配置了以后产生了这个中断信号? ::: image.png

  • GPIO配置了三组寄存器,我们先看一下中断的产生,它包括三个部分:
  • 中断的使能、中断的极性和中断的类型,这个时候我们就需要思考如何触发这样的一些功能?并进行监测和比较。

    当中断被触发了以后,与中断相关的一个状态寄存器会被拉高。同时会导致中断寄存器上的信号被拉高。结果就是组合的一个INT也被拉高。我们可以清除int的状态,通过使用interupt handler将INTCLEAR这个寄存器写成1,这样就处理了这个中断信号。此外,INTSTATUS和INTCLEAR的地址是一样的,读的话得到的就是status,写的话就是一个清除。同一个地址映射到了不同的寄存器【寄存器就是我们的功能窗口】

【PS】中断处理呢? 当我们产生一个中断信号以后,就会举手,当举手的状态被处理器看到以后,处理器就会优先服务于我们的中断,为了相应中断的这个服务程序被称为interrupt handler。

Mask相关的逻辑

image.png
回顾这个与参数相关的表格,当alteernate_func的参数为16’hFFFF时,这个alternate function signals是来自于外部的其他地方:另一个GPIO,静态信号/其他的模块。

Mask的逻辑就是在16bit的数据中,我们一些bit位可能是无用的,所以需要将他们掩盖起来。在这里面更加被细化为我们对哪些bit位写,哪些bit位不写,因为不同的bit位对应了不同的驱动源。

如果我们不做掩位的话,当我们做一个读出来的操作,再将读出来的操作重新写入的时候是有风险的,可能在同一时间有其他的IO或者模块对这条信号线进行了修改,导致本来应该在掩位的bit位的数值也发生了变化。为了使得这个线程更加安全,我们就明确只需要写哪一位,不管其他的位置如何,对其他的位置进行掩盖,这样做是更安全的,每一位也更加稳定。


image.png
image.png

通过这样一个掩位的操作,我们将16bit的IO分为了两个部分,低8位和高8位部分

【问题1】

  • 这里最巧妙的地方在于,我们将bit_mask分为了高8位和低8位以后,我们不用这个寄存器的地址对应的data来判断mask,而是采用寄存器地址的[9:2]bit,将它映射到mask的每一个bit上。
  • 在高8位的时候,mask对应的bit是8位,addr的低两位我们默认是0,因为地址对齐的原因我们不关心它的数值,所以这正好能形成一个映射的关系。
  • 这样子我们就就需要思考是否需要对这个地址进行读写操作?

【问题2】 一般来说一个寄存器地址对应了一个寄存器,但是这里无论是mask的高位地址还是低位地址,他们的地址都是一个范围,那么是存在这么多位的寄存器吗?还是说只有一个寄存器呢?实际上我们认为它是一个寄存器的,只不过mask的值是如何映射到这个寄存器上是值思得思考的。


image.png
分析上面这个过程:

  • GPIOOUT驱动的是0x32E8,低8位看得就是E8也就是1110_1000;
  • 我们最终想要得到的数据就是:将低两位设置为1,高两位清除为0,这样得到的数据就是0010_1011,那么如何得到这个数据呢?
    • bit掩位我们给定的是b11000011,因为我们只关心高两位和低两位的数据,所以对其他的位数不关心,根据低掩位的定义,我们需要将bit mask向左移动两位作为它的偏移地址,所以是0xC3*4;
    • 当我们该数据索引到的低byte地址的赋值为0000_0011,也就是对最低两位进行了修改,所以得到的数据就是0010_1011。
  • 得到了地址以后我们就可以对它进行一个访问,对这个地址的访问,将LOWBYTE改成了C3;

image.png
同样的,我们理解一下对于高BYTE掩位的操作:

  • 当GPIOOUT驱动为0x322B,高8位看得就是32,也就是0011_0010;
  • 我们将它的[12:11]设置为0,最高为设置为1,得到的就是1010_0010;
  • bit掩位提供的是10011000,对应该掩位地址上我们修改的值实际上是1000_0000,也就是对最高位进行了修改;
  • 所以最后得到的数据就是1010_0010,也就是0XA2; :::info 【总结】
    实际上我们的理解是这里对它一共进行了两步:

  • 第一步是产生一个bit掩位,通过这个bit掩位找到掩位的地址,使用该地址进行索引;

  • 对该mask_byte对应的地址写入一个数据,将该数据的逻辑直接导入到data_out,所以不用写data_out了。这样节省了访问寄存器的时间。 :::

读写操作分析

image.png

data寄存器的写就是往dataout中去写,还有一个就是读回来,同步的实现需要双flip-flop来实现; data_out是向出写(是图中左边那一块中的寄存器),写出去的一定是和GPIO的clk是同步的,但是读回来的就不一定是同步信号了。

【思考】

  • 我们可以看到两个寄存器都可以来完成读写的操作,那么他们什么时候完成读写操作呢?I/O Pad可以同时完成读和写操作呢?这不行,在一个时间点里只能完成以此数据交互的操作。那么如何判断在一个时间内是读数据操作还是写数据操作呢,这个时候需要串口,follow这个时序协议才能成功读写数据。
  • 如果没有PIN_MUX来与外部的I/O PAD进行交互的话,那么可以不考虑这种情况,可以同时完成读写操作。

image.png

对于OUTENSET寄存器而言:

  • 如果进行的是写操作,那么就是将ouput设置为1;

对于OUTENCLR寄存器而言:

  • 它CLR的意思不是将OUTENSET写0,写0是没有用的。
  • 对于OUTENSET而言,默认是0,也就是一个读的动作;

:::info 如果我们没有对PORTEN进行配置,那么此时默认的方向就是PORTIN的,dataout此时是否能成功传递到右边部分并从PORTOUT中传出去呢? :::

Alternative function分析

image.png

在有MUX的情况下,将altfuncset置位1,此时外部的PIN_MUX就发挥了作用,否则就是对这个寄存器进行一个简单的测试。