用FPGA为AG32VF407定制才是这芯片最为强大的功能了,没有之一
与上一篇【AG32VF407测评】FPGA工程初探使用FPGA控制GPIO的不同之处在于,本篇实现的FPGA逻辑是可以被MCU所控制的,而上一篇MCU并没有参与到控制之中,LED完全由FPGA逻辑控制。
先理清楚几种IP,这里仅代表我自己的理解,与官方无关,如果错误,请予指正
首先是芯片IP,我现在还没有搞清楚AG32VF407到底是独立于FPGA芯片之外的独立IP核还是在FPGA上实现的硬核或者软核,这关系到修改FPGA的逻辑是否会覆盖芯片核,我大概的理解应该是类似Zynq那样的RISC-V硬核+FPGA的结构,硬核的结构是固化的不可修改的,即修改FPGA的逻辑并不会删除MCU的逻辑,由这个芯片IP我们就可以像普通的MCU那样对其进行软件上的开发。
然后官方提供的几个预定义IP,即官方做的独立于芯片IP之外的IP,可以理解为可拆卸的外设,这些IP是官方提供的可选功能,目前我理解的有ahb_slave和analog两个,可能还有其它,但我还不能理解透彻,这几个预定义IPplatformio.ini中通过ip_name属性引入。
最后就是自定义的IP了,就是我们用户自己开发定制的IP,这里也可以分两种,一种是包含了芯片IP的完全体IP,这种生成的HDL文件中会包含对芯片IP的调用,好处是可以对芯片预定义配置,例如我们在上面加入LED闪烁的逻辑,那么当我们把逻辑下载到芯片内,不需要对芯片进行软件变成,LED就能自动闪烁
还有一种官方称为可移植IP,这种不包含芯片IP,跟上面的预定义IP一样,相当于一个可扩展的功能包,好处在于与MCU解耦,可以专注于定制外设的开发而不对MCU产生多余的影响。下面的例子我们使用可移植IP开发一个简易的定制LED外设
下面的例子基于《agrv2k 逻辑设置-.pdf》这篇文档与官方的ahb_slave示例,文档可以在AGM32 peripharals特性文档中找到,ahb_slave在源码的examples目录下
1、工程准备
新建一个ip_led的目录,从ahb_slave目录下复制platformio.ini、top.ve文件和src目录到该目录下,然后vs_code打开这个目录
清空top.ve,并添加以下内容,LED_OUT 是我们LED控制器所控制的LED引脚, OUTPUT表示该引脚为输出
SYSCLK 120
HSECLK 8
LED_OUT PIN_32:OUTPUT # LED1
打开并修改platformio.ini,主要修改ip_name属性为ip_led,并添加logic_ip = true属性,使其成为可移植IP
然后使用platformio命令生成FPGA工程,在VS_Code右侧栏中单机platformio图标,选择release——Custom——Prepare IP
完成后会自动生成logic文件夹,用quartus打开logic文件夹下的ip_led.qpf工程文件,现在我们就可以开发我们的自定义外设了
2、自定义LED外设逻辑
我们先实现一些简单的LED外设功能,对于MCU上的外设,大多时候我们都是通过寄存器的方式进行控制的,一般常见的寄存器包括控制、状态和参数等,我们先定义一个LED外设的寄存器结构体,如下:
struct LED_Typedef
{
uint32_t ctrl; // 控制寄存器
uint32_t status; // 状态寄存器
uint32_t period; // PWM周期寄存器
uint32_t duty; // PWM占空比寄存器
};
上面就是我们需要在FPGA逻辑上实现的四个寄存器,AG32VF407地址位宽为32,所以我们也使用32位的寄存器,这其中ctrl控制寄存器我们使用了三个位控制LED外设的功能
其中bit[0]为外设使能位,置1即可使能LED外设,bit[1]为输出模式,为0使用纯电平,为1使用PWM,bit[2]设置LED引脚当前输出电平
status寄存器只使用了bit[0],用于指示当前的LED引脚的电平,peroid和duty为PWM模式下才会使用,位宽都为32位。
在介绍LED外设的HDL代码之前我们还需要先简单了解下MCU与FPGA交互的协议,即AHB和APB协议,他们都是ARM推出的AMBA总线规范的一部分,在ARM官网上就能免费下载其规范说明文档,这些协议都是芯片内部的之间的通信协议,其中AHB用于高速外设,APB用于低速外设,从结构上看,MCU可以直接访问AHP,而APB则需要从过AHB转接,在我们的IP中,为了方便,就直接使用AHB协议,而不是APB,即便我们LED外设应该属于低速外设,AHB具体的协议内容较多,这里不展开,感兴趣的可以自行研究
熟悉ARM芯片开发的应该都清楚,我们访问外设都是通过一个内存地址进行访问的,同样,我们也需要将我们的LED外设映射到一个可以被AHP协议访问的地址上去,在AG32VF407的Datasheet文档中可以找到内存映射的说明,External AHB就是用于扩展使用内存地址空间,使用这个地址不会与已有的外设冲突,我们的LED外设直接使用0x60000000这个地址作为外设地址了
确定好寄存器和内存地址后就可以进行FPGA逻辑开发了,先提供大体的开发思路
首先是需要对接与MCU通信的AHB协议,可以通过研究ahb_slave示例来了解AHB协议的使用,我们的外设是作为AHB的从机,需要根据协议要求响应主机,根据AHB传输过来的地址和数据确定主机想要访问的寄存器并进行相应的处理
然后将主机对寄存器的设置反应到外设功能上,对于我们的LED外设,就是设置LED引脚的状态,根据PWM相关寄存器设置的参数产生PWM时序
最后就是同步外设的状态到状态寄存器上,如果外设有DMA和中断等功能则需要进行相应的处理
我的代码里面主要包括三个文件,也即3个主要的模块,最顶层的是ip_led模块负责对接AHB协议,解析寄存器地址,led_ctrl负责LED外设的主要行为逻辑,pwm模块主要生成PWM时序,具体的代码在贴文最后会给出,代码本身不复杂,但需要熟悉FPGA的开发逻辑
代码写好后简易现在quartus上编译一下以检查错误,正式的编译是在supra软件,可以参考上篇,打开工程进行编译即可,要注意,supra打开的工程实在logic目录下的
编译好后来到VS_Code,先编译一下,然后使用Update_Logic将硬件逻辑固化到芯片上,然后我们开发一个示例来操作我们LED外设
3、软件开发
在src/main.c中编写一下代码
#include "board.h"
struct LED_Typedef
{
uint32_t ctrl; // 控制寄存器
uint32_t status; // 状态寄存器
uint32_t period; // PWM周期寄存器
uint32_t duty; // PWM占空比寄存器
};
int main(void)
{
// uint32_t status ;
// This will init clock and uart on the board
board_init();
struct LED_Typedef *LED= (struct LED_Typedef*)MMIO_BASE;
// LED->period = 120 * 1000;
// LED->duty = 30 * 1000;
LED->ctrl = 0x01U;
while (1) {
UTIL_IdleUs(100e3);
LED->ctrl = 0x01U;
UTIL_IdleUs(100e3);
LED->ctrl = 0x05U;
// status = LED->status;
}
}
这里使用LED外设控制板上的LED灯闪烁,编译下载,可以看到LED闪烁。
这里我也测试status寄存器读取和PWM模式,但并未成功,FPGA代码还不完善,后续有时间再做修改了。