AG32mcu通过寄存器方式操作cpld
从这个案例开始,需要了解AHB总线的基础知识了。如果对AHB总线不是很了解,可以自行百度再学习下。这里有个比较好的讲解:AMBA总线—AHB总线协议详解_ahb协议-CSDN博客
这里只描述用到的部分:
在AG32芯片内部,可以认为:cpu、ram、cpld、dma,这四个部分是挂在ahb总线下的。其他的外设都是挂在apb总线下的。
挂在ahb下的4部分,cpu、dma、cpld 这3个会成为master端,可能来抢占ahb总线。
在真实使用中,cpld经常被用于slave端,有点类似于“外设”,但却是挂在ahb下的。既然cpld是挂在ahb下的,那么它的接口就是要符合标准ahb访问协议的。
那么,当mcu来访问cpld的数据时,cpld就要根据这个接口来做对应的处理。
整个访问过程分为两部分:
1. mcu如何访问cpld的“寄存器”;
2. 当mcu访问时,cpld如何判别及响应;
分别描述。
1. mcu如何访问cpld
这部分很简单。
AG32在对地址编码中,cpld的地址区间设定为:0x60000000 ~ 0x7FFFFFFF
就像ram的地址区间是 0x20000000 ~ 0x20020000,flash的地址区间是0x80000000 ~ 0x800x0000 一样。
当mcu对大于0x60000000这个区间内的地址访问时,解码器会自动丢给cpld的接口。这时mcu就相当于访问了cpld的“寄存器”。
mcu是全局寻址,对这个空间的访问和对ram(0x20000000起)空间的访问是一样的方式,在C代码中,可以这样写:
读cpld:int cpRdReg = *((int *)0x60000000);
写cpld:*((int *)0x60000004) = cpWtReg;
Mcu端读写cpld,直接通过上述语句就可以了。理解和操作上都比较简单。
2. cpld如何判别及响应
这部分描述起来比较麻烦。总体是:cpld被ahb总线接口触发,并且回应要遵循ahb总线协议。
当上述mcu读写动作发生时,AHB总线会把动作拆解为读写信号,传递到analog_ip.v(新版本是user_ip.v)的接口,用户cpld程序需要响应该信号。
以下,以写动作 *((int *)0x60000004) = cpWtReg 为例,描述cpld端会发生的事情。
回顾下analog_ip.v中的接口部分:
其中slave_ahb_开头的一组信号,是cpld作为主端时用的,暂时不用理会。
Mem_ahb_开头的一组信号,是cpld作为从端使用的。
当mcu有读写操作时,mem_ahb_这组信号将发生变化。
几个信号的概述(更详细的讲解请自行百度):
- Ahb_htrans: 当前传输类型(00: IDLE、01: BUSY、10: NONSEQ、11: SEQ)
- Ahb_ready:mcu读时要mcu要准备好cpld才会写
- Ahb_hwrite: 要读还是要写(1为写,0为读)
- Ahb_haddr[32]: 要操作的地址
- Ahb_hsize:transfer的大小,以字节为单位
- Ahb_hburst:批量传输
- Ahb_hwdata[32]:写的数据,32位
- Ahb_hreadyout:输出信号,mcu写时cpld是否准备好
- Ahb_hresp:输出信号,响应信号(OK、retry、error、split)
- Ahb_hrdata[32]:读的数据,32位
根据AHB时序,在一次传输中,cpld(slave端)会先拿到addr地址,读/写的标记,然后交互ready信号后,开始数据传输。
大致如下图(无等待类型的图):
比如,mcu要读0x60000004的寄存器:
mcu端直接C语言这样调用:int cpRdReg = *((int *)0x60000004);
cpld端,可以根据以上信号做如下处理:
----------------------------------------------
//mcu的读操作响应
//mcu端用C语言:int value = *((int *)0x60000004);
reg [31:0] hrdata_reg; //定义32位的hrdata_reg
always @(posedge sys_clock) begin //clk上升沿触发
if (mem_ahb_htrans == 2'b10 && //NONSEQ状态,第一次传输
mem_ahb_hready && //master已ready,可以给数据线写入了
!mem_ahb_hwrite && //读 (0 读,1 写)
mem_ahb_haddr[23:0] == 'h04) //读地址为0x60000004(cpld内部用相对偏移)。
begin
hrdata_reg <= hwdata_reg; //把另一准备好的数据给到hrdata_reg
end
end
assign mem_ahb_hrdata = hrdata_reg; //绑定hrdata_reg到读的数据线上-----------------------------------------------
以上代码,加入到analog_ip.v的module下,就可以完成cpld对mcu读动作的响应。
比如,mcu要写0x60000000的寄存器:
mcu端直接C语言这样调用:*((int *)0x60000000) = value;
cpld端,可以根据以上信号做如下处理:
----------------------------------------------
//mcu的写操作响应
//mcu端用C语言:*((int *)0x60000000) = value;
reg [0:0] isNewAction = 0;
reg [31:0] hwdata_reg; //定义32位的hwdata_reg
always @(posedge sys_clock) begin //clk上升沿触发
if (mem_ahb_htrans == 2'b10) //NONSEQ状态,第一次传输
begin
isNewAction <= 1;
end
if (mem_ahb_htrans == 2'b00 && //IDLE状态,真正开始写
isNewAction && //新动作
mem_ahb_hwrite && //写 (0 读,1 写)
mem_ahb_haddr[23:0] == 'h00) //写地址为0x60000000(cpld内部用相对偏移)。
begin
hwdata_reg <= mem_ahb_hwdata; //把收到的数据给到hwdata_reg
isNewAction <= 0;
end
end//这个过程,是把mcu写进来的数据收到hwdata_reg中
-----------------------------------------------
这部分的实例代码,请参考网盘上获取:
链接:百度网盘 请输入提取码 里边的 \cpld-fpga文档\logic样例\5.mcu读写cpld寄存器
注意:这里展示的,仅仅是基于AHB总线上的数据交互。
在实际应用中,比如要实现一个串口之类的,往往是慢速设备,这些是要挂载到apb上的。慢速设备要经过ahb到apb的bridge,才能最终使用。请继续往下看。