AG32 MCU中CPLD使用基础(二)

浏览: 作者:Rocky 来源:AGM 时间:2025-03-16 分类:FPGA应用

AG32 MCU中CPLD使用基础(二)

 

目录

 

一、mcu与cpld的交互

1. mcu传递信号给cpld;
2. cpld传递信号给mcu;
3. mcu从cpld里读写数据;

二、 mcu与cpld的apb数据交互 
三、 ADC样例与UartTx样例
四、 dma在cpld中的使用
五、 cpld中使用ram
六、 一些小技巧:如何定义信号量数组

 

一、MCU与CPLD交互


cpld工程创建及编译的操作流程,参考文档《AG32下fpga和cpld的使用入门》
在工程中,用户逻辑部分编写是从analog_ip.v的接口下开始的。


mcu和cpld之间的交互,可以分为:
1. mcu传递信号给cpld;(如mcu的gpio传递高低信号到cpld)
2. cpld传递信号给mcu;(如:对mcu产生中断信号)
3. mcu读写数据到cpld;
4. 不建议,cpld做为主设备对mcu写。


也就是说,在mcu和cpld交互中,cpld更像一个外设。
其中,前两种较为简单。后两种要使用AHB总线来操作。


下边针对四种情况分别说明:

1. mcu传递信号给cpld;


这种使用较简单。步骤如下:
ve中定义信号:

GPIO4_1 iocvt_chn:OUTPUT

表示,用mcu的gpio(gpio4_1)来输入信号到cpld。
然后,prepare LOGIC工程后,可以看到analog_ip.v接口中的信号:

input iocvt_chn_out_data,
input iocvt_chn_out_en,
这里的iocvt_chn_out_data,就是对接到mcu的gpio4_1的信号。


当控制mcu的gpio4_1高低切换时,cpld中的iocvt_chn_out_data,会对应来变化。

具体样例,可以参考网盘“logic样例\3.mcu信号到cpld到pin”的样例,该样例中,展示了mcu控制cpld继续控制led的过程。
除了gpio信号输出到cpld,其他比如pwm输出信号等,都可以输入到 cpld。


2. cpld传递信号给mcu;


这种方式和1相近,只不过是反向。
可以在mcu中定义gpio4_2为输入并使能中断,则cpld中设置信号高低时,将触发mcu 的中断。


VE中定义信号:
GPIO4_2 iocvt_chn:INPUT
表示,用mcu的gpio(gpio4_2)信号来源于cpld的iocvt_chn。
然后prepare LOGIC程后,可以看到analog_ip.v接口中的信号:


output         iocvt_chn_in_data,


这里的iocvt_chn_in_data,就是对接到mcu的gpio4_2的信号。
cpld中控制iocvt_chn_in_data信号高低时,mcu中的gpio4_2对应变化。
这里不再举例。

 

3. mcu读写数据到cpld;


在地址设计中cpld的地址区间是:

0x60000000 ~ 0x7FFFFFFF

mcu对这个区间内的地址访问时,相当于访问了cpld的“寄存器”。


mcu是全局寻址,对这个空间的访问和对ram(0x20000000 起)空间的访问是一样的方式,在C代码中,可以这样写:

cpld:int cpRdReg = *((int *)0x60000000);
cpld:*((int *)0x60000004) = cpWtReg;


MCU端读写cpld较为简单,直接通过上述语句就可以了。

mcu读写动作发生时,cpld端是如何反应的?


当上述mcu读写动作发生时,AHB总线会把动作拆解为读写信号,传递到analog_ip.v的接口,用户cpld程序需要响应该信号。

以下,以写动作 *((int *)0x60000004) = cpWtReg 为例,描述 cpld端会发生的事情。

回顾下analog_ip.v中的接口部分:

 

21

 

其中slave_ahb_开头的一组信号,是cpld作为主端时用的,暂时不用理会。
Mem_ahb_开头的一组信号,是cpld作为从端使用的。
mcu有读写操作时,mem_ahb_这组信号将发生变化。


这部分是遵循标准的AHB总线协议的。如果对AHB总线印象不深,请自行百度。可参考的讲解:

https://blog.csdn.net/weixin_46022434/article/details/104987905


几个信号的概述(更详细的讲解请自行百度):

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 信号后,开始数据传输。
大致如下图(无等待类型的图):


22 

比如,mcu要读0x60000004的寄存器:

mcu端直接C语言这样调用:

int cpRdReg = *((int *)0x60000004);

cpld端,可以根据以上信号做如下处理:


23 

以上代码,加入到analog_ip.v的module下,就可以完成cpld对 mcu读动作的响应。


比如,mcu要写0x60000000的寄存器:

mcu端直接C语言这样调用:

*((int *)0x60000000) = value;

cpld端,可以根据以上信号做如下处理:


24 

这部分的实例代码,请参考网盘上cpld样例工程《5.mcu读写cpld 寄存器》。


注意:这里展示的,仅仅是基于AHB总线上的数据交互。
在实际应用中,比如要实现一个串口之类的,往往是慢速设备,这些是要挂载到apb上的。慢速设备要经过ahb到apb的bridge,才能最终使用。请继续往下看。

 

二、MCU与cpld的apb数据交互


上节讲述了 mcu 和 cpld 之间交互数据的实现方式。
但数据是在 ahb 层面的响应,慢速设备不能直接使用。

慢速设备需要 ahb 转为 apb 后,使用 apb 的信号来交互。这种情况,转变为mcu和apb之间的交互。


mcu和apb之间的交互,相比mcu和aph之间的交互,多了一层 ahb到apb的转换。这个转换是借助于ahb2apb.v模块来实现的(在 example/analog下找该.v文件)。
该模块:输入是ahb的一组信号,输出是apb的一组信号。使用如下图:


25 


如果实现mcu和apb的交互,则需要操作的是转换后的这组apb信号。

关于 apb 总线的使用,更多信息请自行百度。


这里只是简述下apb信号列表(与ahb略有不同):
apb_psel:片选
apb_penable:表示传输进入第二周期(准备好了读/写)
apb_pwrite:传输方向(1-写;0-读)
apb_paddr[32]:地址总线,要操作的地址
apb_pwdata[32]:写的数据,32 位
apb_prdata[32]:读的数据,32 位


以下展示在apb下如何实现跟mcu的交互,仍以ahb的两个寄存器为例。


1. 首先需要增加 ahb 转 apb 的信号关联;


如上图。
Ahb2apb模块会把ahb信号转换为apb信号。接下来操作apb信号即可。


2. 在转换后的apb信号中,实现写和读的操作。


mcu 读操作时:
比如,mcu要读0x60000004的寄存器:
mcu 端直接 C 语言这样调用:

int cpRdReg = *((int *)0x60000004);
cpld 端,可以根据以上信号做如下处理:


26 


mcu 写操作时:
比如,mcu要写0x60000000的寄存器:
mcu 端直接 C 语言这样调用:

*((int *)0x60000000) = value;
cpld 端,可以根据以上信号做如下处理:


27 


这个功能实现后,其实是个简单的“空外设”。可以用它做为实现复杂功能外设的基础。这部分的实例代码,请参考网盘上 cpld 样例工程《5.mcu 读写 cpld 寄存器》。


样例展示到这里,mcu和cpld的交互上:交互信号、跟ahb交互数据、跟apb交互数据,基本的交互通路已经建立。
接下来,用户根据自己的需求,在 cpld 中交互到数据后,编写自己需要的功能即可。

 

 

三、ADC样例与UartTx样例

 

这里借助ADC样例和UartTx 样例,展示下更多的使用方法。
ADC和UartTx,都是接到apb的。
功能方面,都包括:设置寄存器,读取寄存器。


ADC 样例:

SDK 安装后的example/analog工程,展示了ADC模块做为外设,与 mcu 之间的数据交互(ADC 采集后的数据,被mcu读取)。


这里的ADC模块,在cpld里做的工作,是把串行数据转换成并行数据。
因为 AG32 芯 片中集成的 ADC 硬核是串行数据输出的,而 mcu 访问数据都是并行的。为了实现mcu端访问数据的统一,这里增加了ADC 的 cpld 代码,实现该串行转并行的功能。
相当于:ADC硬核+ADC的cpld逻辑,实现了一个完整的“ADC 外设”。

 

ADC的用户cpld代码,都是在 analog_ip.v中实现的。
直接以analog_ip.v中的实现来说明:


1. analog_ip的接口下边,首先是ahb2apb的信号转换。
转换后,后续的ADC/DAC/CMP,都是以 apb 信号线来操作。


2. 定义出信号线数组,用于连接实例化的6个对象:3个ADC/2个DAC/1个CMP;
这个里边,wire [PER_CNT-1:0] select 是定义片选,注意它的取位,这里取值后的数组,分别对应到

’h000,’h1000,’h2000,’h3000’h4000,’h5000
其他数组,都分别对应 6 个实例化以后的连线。


3. 然后用 for 循环 genarate 实例化 ADC/DAC/CMP;
包含 3 个 ADC,2 个 DAC,1 个 CMP。
实例化时,连线都对应到上边定义的数组中去了。


4. 接下来的 pr_select,是记录当前片选是哪个。
索引值:0 ADC0;1 ADC1;2 ADC2;3 DAC0;4 DAC1;5 CMP


5. 接下来的 apb_prdata,


6. 再接下来,就是 3 个模块 ADC/DAC/CMP 各自的实现了。
具体内容不再解析。

 

注意几个点:


A. mcu中设置寄存器时,都是值下来后,在ADC/DAC/CMP中缓存到reg中;
如:0x60005004是CMP的CHANNAL寄存器,mcu对这里寄存器设置值时,
最终cpld里执行:

 

28 

B. mcu中要获取的值,也类似传递。


UartTx 样例: 

这个样例比ADC样例简单些。
这个样例中,实现一个串口功能(只有TX功能,固定为115200频率)。
mcu来说,它也是cpld实现的一个“外设”。
它的 cpld 实现逻辑,跟上边的ADC样例思路是相同的,都是先 ahb 转 apb,然后实例化外设,mcu 写数据时 apb 接收后处理,mcu 读数据时 apb 触发后处理。


读的时候,是读的串口的 state 状态;
写的时候,会把写的数据继续丢给 uart 模块处理(转化为 IO 的高低波形输出)

mcu 端,在 while(1)里边:
查询 cpld的写状态,当状态合适时,发数据给 cpld,cpld 根据时序转换为波形输出到定义的PIN。


更多细节,请参考 cpld 工程中的代码和注释。
该工程位于网盘上“logic样例/6.UartTx 例程”。


四、DMA在CPLD中的使用

 

CPLD中实现DMA的逻辑:


1. MCU为 master,cpld为slave,mcu对cpld的交互方式为存取寄存器的方式;
2. mcu中配置好DMA(读取 cpld 中准备好的数据);
3. cpld中准备好数据后,触发dma信号,dma自动搬运到mcu指定的 ram;
4. 搬运一次后,dma给cpld一个clear信号,完成一次dma搬运;
5. 等到cpld中再次准备好数据,将再次触发dma信号,重复3和4;


对于cpld来说,mcu来读取数据和dma来读取数据,是一致的。
dma来读取时,只是每次读完后会多给cpld一个clear信号。

 

更多细节,请参考网盘上《7.cpld 中配合实现 mcu 的 dma 读取》部分的样例。
在这个样例中,展示了两部分代码:


1. mcu中,配置dma读取;为了测试,mcu会在另一地址给cpld写数据;
2. cpld中,会对mcu写进来的数据缓存,缓存后触发dma的信号,让dma来读取数据。而dma从cpld里读取数据后会给cpld一个clear 信号,标志一次 dma 交互完成。

 

五、CPLD中使用ram

 

cpld本身有自带的ram,为4个M9K块,每个M9K大小为8192 bits。
(即:4个M9K 总空间为4Kbytes)
更详细信息,请参考《AGRV2K_Rev2.0.pdf》中的说明。

 

29 

除了cpld自带的内存,cpld还可以使用mcu的内存sram。
AG32整个芯片系列,内存sram大小都是128K。


如果mcu用不了128K,希望分一些给cpld来用,比如,分出来32K给 cpld。可以按照如下方式设置:

 

1. 限制mcu的使用,让mcu只使用前96K;


限制mcu对ram的使用,需要修改ld配置(分散加载相关)来实现。
在路径:AgRV_pio\packages\framework-agrv_sdk\misc\devices下,在文件AgRV2K_mem.ld中可以看到定义如图:

30 

如果只用96K,则修改上边的SRAM_SIZE=96K即可。
修改文件并保存后,需要重启VSCODE工程,让设置项使能。


2. cpld中对后32K的使用;


cpld 使用后 32K,起始地址是从 0x20000000 + 96K 的地址开始。
即:从 0x20018000开始,长度32K,到 0x20020000 结束。
cpld 中对于 sram 的寻址方式和 mcu 相同。
cpld对sram的读写,请参考:
SDK 下的 examples\custom_ip\logic\ram2ahb.v 和 ahb2ram.v

 

31 

六、小技巧

 

如何定义信号量数组:如果cpld里想用信号量数组,并使每个元素对应到不同PIN,可以在 ve 里定义如下:
mcu_a[0] PIN_31
mcu_a[1] PIN_32
mcu_a[2] PIN_33
mcu_a[3] PIN_34
...
如图:

 

32 

 

那么,prepar LOGIC后,将在自动生成的cpld框架.v接口里,表示如下:

 

33 

 

这里就表现为数组的形式了。

 

联系海振远科技

 

电话:  0755-2780 9180 ;  15323895320 ;

邮箱:  tech@hizyuan.com

  Lucy@hizyuan.com