CORE学习:AMBA3--APB总线协议及简单例子
AMBA3—APB总线
APB总线是AMBA里面最简单的一个总线接口了,它是一个非流水线结构,且控制逻辑简单,这也就决定了它是利用于低带宽的外围总线设备上,例如UART、IIC、定时器等等。注意,APB还有一个特点就是,APB的主机只有一个,那就是APB总线桥,不可能有其他主机,也不可能有多个主机。
¶APB状态机
APB总线接口的状态转换图如图所示(ARM IHI 0024B - Page3-2):
可见,当总线复位后,APB进入IDLE状态,然后根据控制端口的输入切换状态,输出的数据和状态以及输入数据有关,因此是一个典型的Mealy型状态机,对它的详细解释如下:
- IDLE:APB 的默认状态,也就是没有传输时候的状态;
- SETUP:在IDLE状态下,将信号PSELx拉高(x是从设备选择信号),进入SETUP状态,收到PENABLE信号进入下一状态;
- ACCESS:传输状态,根据PWRITE、PADDR、PWDATA/PRDATA写入/读取寄存器。完成后将PREADY拉高,PSELx未选中这个APB从机的话退回IDLE,选中但不传输数据(PENABLE拉低)的话退回SETUO;未完成的话PREADY拉低,并在下一个上升沿继续进入ACCESS传输数据;
整个状态很简单,三言两语就可以描述清楚了,那我们再回来看看APB接口是由哪些输入输出信号组成的:
1 | //apb_interface |
¶传输时序
根据这些信号的不同组合,又可以分为带等待信号的数据输入/输出以及不带等待信号的输入/输出,接下来根据ARM的官方手册ARM IHI 0024B对这四种情况进行描(fan)述(yi):
¶不带等待信号的数据写入时序
T1上升沿:拉高PSEL,选中这个APB从机,并且拉高PWRITE,表示接下来要写入数据给从机,并且将数据地址放在PADDR,数据放在PWDATA,从IDLE进入SETUP
T2上升沿:PENABLE拉高,从SETUP进入ACCESS,进行一次数据传输
T3上升沿:PREADY被从机拉高,说明传输完成,PSEL和PENABLE可以拉低
¶带等待信号的写入时序
T1上升沿:拉高PSEL,选中这个APB从机,并且拉高PWRITE,表示接下来要写入数据给从机,并且将数据地址放在PADDR,数据放在PWDATA,从IDLE进入SETUP
T2上升沿:PENABLE拉高,从SETUP进入ACCESS,进行一次数据传输
T3上升沿:PREADY为低,说明数据还未写完/过程未处理完,需要等待一个时钟周期,再次进入ACCESS
T4上升沿:PREADY还是低,再次进入ACCESS
T5上升沿:PREADY被拉高,从机完成接受数据,PSEL和PENABLE拉低
¶读取时序
读取时序的过程与写入时序基本一致,只不过读取时是将PWRITE拉低,而写入是将它拉低,这里不再详细分析时序图,有兴趣可以看官方文档,把图放在这里:
¶动手写一个APB外设
APB外设的逻辑状态和时序已经清楚了,接下来就自己动手写一个APB从机试试吧😍
¶简单的APB从机
先确定好从机需要访问到的寄存器,在这里我定义四个寄存器来模拟:
1 | reg [31:0] readonly32; //0x10000000,只可读 |
实际上这四个寄存器可以通过改改名字和例化其他模块来实现APB外设的所有功能,比如将readwrite16中的16个位分别设置成UART输出模块的波特率控制位、传输帧设置位、中断使能位等等,readwrite32设置为UART输出信息的缓冲寄存器,这样可以实现一个简单的APB-UART外设
当然,这里我只想模拟下读写寄存器,就不需要例化其他模块了,完整的代码贴在下面:
1 |
|
¶仿真测试一下
利用Vivado跑个行为仿真看看:
一点点来分析:
0ns~15ns:reset_n信号置0以后拉高,初始化APB从机
15ns:拉高了psel,模拟选中这个APB从机,即将进入SETUP状态
35ns:拉高了penable,即将进入ACCESS状态,但我并没有给出paddr以及pwrite,因此输出全0
55ns:pwrite拉高,paddr设为0x10000008,pwdata设为0x1234ffff,表明我即将向地址为0x10000008的寄存器写入0x1234ffff,这个地址的寄存器是readwrite32,可以看到这个时候它的值还是全0
57.5ns:可以看到readwrite32寄存器的值已经变成0x1234ffff,且pready已经置1(其实图中pready早就置1了而且没变过,这个图有bug,后来代码改了)
85ns:pwrite拉低,paddr设为0x10000000,表明我要读取地址为0x10000000的寄存器readonly32的数据
87.5ns:prdata输出0x12345678,这和我们设置的readonly32寄存器初始值相同
145ns:pwrite拉高,paddr设为0x1000000C,pwdata设为0x1234abcd,表明我即将向地址为0x1000000C的16位寄存器写入0x1234ffff,这个地址的寄存器是readwrite16,可以看到这个时候它的值还是全0
147.5ns:readwrite16寄存器的值已经变成0xabcd,成功地写入低16位到寄存器
185ns:尝试读取未定义的寄存器,地址为0x10000010
187.5ns:pslverr被置1,出现错误,功能正常
205ns:尝试读取地址为0x10000004的只读寄存器readonly16
207.5ns:pslverr重新置0,读取到寄存器的值0x0000abcd,功能正常
所以这个简单的APB外设功能应该没问题,有空把它挂到软核/硬核上看看情况☺️
CORE学习:AMBA3--APB总线协议及简单例子