Skip to content

MCU+CPLD 联合编程(概念及流程)

在使用 MCU+CPLD 联合编程之前,请确认已经熟练掌握 MCU 的使用方法,并且对 CPLD 编程(Verilog 语言)有一定的基础。

另外,对 AHB 总线也需要有一定的了解。

这个章节分为两部分:

  1. 第一部分,展示联合编程中各种概念和操作流程;
  2. 第二部分,从具体案例出发,由浅到深来描述各种常用的编程技巧。

以下描述为第一部分(概念和流程)。如要查看实例讲解,请参考第二部分

一、前述

在 AG32 芯片(所有型号)中,都有内嵌 2K 的逻辑门,可供 FPGA/CPLD 来使用。

也就是说,使用 AG32 的芯片时,有三种选择:

  1. 只使用 MCU 部分;
  2. 只使用 CPLD 部分;
  3. 同时使用 MCU 和 CPLD(即:MCU 和 CPLD 联合编程);

如果:

  • 用于 1(仅用做 MCU),不必关注此文档。
  • 用于 2(仅用做 CPLD),硬件设计和操作流程,请跳转到“纯粹 CPLD 编程”,也不必关注此文档。
  • 用于 3(MCU 和 CPLD 联合编程):请按照该文档的描述,先完整走一个流程。

二、基础了解

  1. AG32 整颗芯片包含两部分:MCU 和 CPLD。

    这两部分是相互独立的(各自编译、各自下载),但又可以相互连通起来(信号连通)。

    芯片要把这两部分的 bin 都烧录进去,才能运行起来。

  2. MCU 和 CPLD 到外部 Pin 脚的连通,是通过 VE 文件来配置的。

    跟 ST 芯片每个 GPIO 对应某个固定 Pin 脚不同,在 AG32 中,所有的 GPIO/大部分的外设,对外引脚并不是定死的。而是需要在 VE 文件中指定对应。

    VE 中,除了配置 GPIO 到 Pin 的关联,还可以配置 MCU 到 CPLD 之间的信号关联。

  3. MCU+CPLD 联合编程时,CPLD 工程是由 VSCode 的“prepare LOGIC”命令自动生成的。

    注意:联合编程时,CPLD 工程不能手工通过 Supra 建立。

    CPLD 的对外接口,也是依托于 VSCode 工程来的,不再是孤立的。

    CPLD 中的 top module 的信号输入,是关联到 MCU 工程的(由 VE 配置出来)。

  4. CPLD 在联合编程中的定位:

    整颗芯片运行时,需要两个 bin:MCU 的 bin 和 CPLD 的 bin。

    如果芯片中只使用 MCU 不使用 CPLD:

    此时,VE 文件里只配置 MCU 用到的 Pin 脚即可。

    这种情况下,VSCode 工程中点“upload LOGIC”时,会自动生成默认 logic(该 logic 中“用户逻辑”为空而已),并编译出 bin 并烧录。

    整个过程中,logic 部分对开发者来说是无感的。

    如果芯片中同时使用 MCU 和 CPLD:

    那么要把工程配置成“自定义 logic”。

    此时 VE 文件里需要配置用到的 MCU 和 Pin 之间、MCU 和 CPLD 之间、CPLD 和 Pin 之间,三种情况下的信号关联。

    这种情况下,VSCode 下点“prepare LOGIC”按钮,会为开发者生成 CPLD 的框架工程。开发者需要在这个框架下完成 CPLD 逻辑的编写。这个编写调试中,开发者持有主动权。等全部开发完成,并编译出 bin,烧录就又回到 VSCode 去烧录。

    这个过程,除了 logic 需要用户自己编写编译外,从更外层的视角看,整个流程还是跟“默认 Logic”是相同的。

三、安装软件

MCU 需要使用到的 VSCode 前边已经安装过了。

FPGA/CPLD 需要使用 Supra 和 Altera Quartus II 两个软件来编程。

其中:

  • Supra.exe 软件在安装完 SDK 后,已经在 SDK 路径下了,可直接使用(无需额外安装 Supra)。

    (注:如果在 SDK 路径下没有找到 Supra.exe,可进入安装路径\AgRV_pio\packages\tool-agrv_logic\bin\去找到。同时建议手工新建一个快捷方式到 SDK 路径下以方便后续使用)

  • Altera Quartus II 软件需要用户自行安装,安装后再安装对应的器件库。

    (注意:Quartus 不能使用 Lite 版本,需要使用 Full 版本。最好使用版本:Quartus II 64-Bit Version 13.0.1 Build 232 06/12/2013 SJ Full Version)

在这两个软件中,

  • Quartus 工具用来编写 Verilog 代码并导出 vo 文件,
  • Supra 工具使用 vo 文件来生成最终的 bin。

四、CPLD 使用流程

从使用角度来说,整体需要 4 个步骤:

  1. 在 VE 文件里配置引脚关系;
  2. 建立 CPLD 空工程(使用 prepare LOGIC 命令)并编写逻辑;
  3. Quartus 下进行工程转换(和综合);
  4. Supra 下编译出最终的 logic.bin;

下边进入详细描述。

1. 在 VE 里定义引脚和信号关系

这部分和 MCU 使用是相仿的。

在 MCU 里,都是 MCU 信号直接到 PIN 脚的映射。比如:

那么,在使用 CPLD 里,除了上述的“MCU 信号到 Pin 脚”关联外,还允许“MCU 信号到 CPLD 信号”和“CPLD 信号到 Pin 脚”的两种关联。这里描述的三种关联,是常见的三种信号关联方式。详细使用方法,后续会介绍。

这里需要知道的是,每次 VE 文件修改完以后,都要重新走一遍整个流程(从 prepare LOGIC 开始)。

其实不光修改完 VE 需要重新走一遍流程,有时修改 platformio.ini 也需要重新走一遍流程(比如,修改管脚数量)。

如果是第一次试验 example 例程,可以不用修改,使用默认的 VE 文件即可。

2. 生成空的 CPLD 工程

仍然以 example(路径:...\AgRV_pio\platforms\AgRV\examples\example)为例建立工程。

在 example 样例程序中,默认是没有打开自定义 IP 的。

首先,要通过配置打开自定义 IP

方法:在 platformio.ini 中打开以下两项:

ini
ip_name = analog_ip
logic_dir = logic

注意:这两行去掉注释的时候,前边不要留空格。要顶格写

打开以上两项并保存文件后,才能在左边栏看到创建 logic 框架工程的选项(prepare LOGIC):

点击该功能【Prepare LOGIC】后,可以看到在 example 工程目录下生成一个 logic 文件夹,自动生成的文件如下图:

这里生成的 logic 文件夹,就是空的 CPLD 工程。也是后续编写 CPLD 的模板工程(后续代码在这个基础上添加)。

关于改写文件名:

这里的文件名字,是根据 platformio.ini 里边的配置项来的:

ini
board_logic.ve = example_board.ve
ip_name = analog_ip
logic_dir = logic

如果想改文件名字,可先在 platformio.ini 中更改名字,更改后再点生成按钮来自动生成。可更改的名字,就是上边的三项:

  • board_logic.ve 对应 logic 文件夹内 example_board.v 的名字
  • ip_name 对应 logic 文件夹内 analog_ip.v 的名字
  • logic_dir 对应文件夹 logic 的文件夹名字

在生成后的文件夹内,注意其中的两个 .v 文件:analog_ip.v 和 example_board.v。

这两个文件是 Verilog 的源码文件。

其中:

  • analog_ip.v 是一份空的模板,用户自己要实现的功能,就在这个空模板上展开;这个空的模板里边,主要就是 module analog_ip 的接口定义。
  • example_board.v 是根据工程中 example_board.ve 里的 pin 配置,Logic prepare 时自动转换出来的 Verilog 源码,也是 Supra 工程的 top module。这部分不要手工改动。

到这里,CPLD 空工程建立完毕。

注意:

后期修改完 VE 的配置后,Prepare LOGIC 时,会重新生成 CPLD 工程模板,这个模板文件都仍然存到 logic 文件夹下,且仍然是这两个 .v 文件。

只不过 analog_ip.v 对应新增出 analog_ip_tmpl.v 文件,而 example_board.v 则是直接覆盖。

由于 analog_ip.v 是用户程序的入口,用户程序会从这里开始写。这个文件一定是会修改的。所以,每次 prepare LOGIC 时,这个文件会对应生成 analog_ip_tmpl.v 文件,而不是覆盖。

生成出 analog_ip_tmpl.v 后,用户要根据自己的情况,把新接口手动合并到 analog_ip.v 去。

注:这里只是以 example 来举例。实际应用中,在导出空工程前,需要先配置好 platformio.ini 的其他项和 example_board.ve 所需要的引脚。包括:board_logic.device 配置 32/48/64/100 脚。

也就是第一步要做的内容。

3. Quartus 下进行工程转换

使用前边安装过的 Quartus II 软件,打开 \example\logic\ 的工程文件。打开后如图:

这里除了 example_board.v 和 analog_ip.v 外,还有个系统的 alta_sim.v,这个文件是提供芯片系统的功能,类似函数库,可不用关注。

此时,这个工程是个空的工程,用户要根据需求在 analog_ip.v 中实现功能。

再次回顾下 3 个 .v 文件:

  • analog_ip.v:用户自定义 logic 的入口。用户 logic 实现在这里展开;
  • example_board.v:整个 logic 的 top module。会关联 analog_ip 的 module 和 alta_sim 下的各 module。不要修改该文件。
  • alta_sim.v:封装过的跟 AG32 相关的各 module。不要修改该文件。

接下来进行空工程的转换和编译。

注意:如果不是很熟练,这里的空工程最好不要马上添加自己的代码。而是应该首先工程转换。

工程转换的目的,是把 CPLD 模板工程真正转换成 Quartus 实际运行的状态。

操作步骤:

打开 Quartus 菜单的【Tools】->【Tcl Scripts...】,弹出框如下图:

【Run】运行成功后,可以看到该 logic 占用的资源数量。

这个转换的过程,顺便进行了 CPLD 工程的编译。

上图中斜杠前边标识的大小就是所占的逻辑单元数(后续使用中逻辑单元不能超过 2K,严格讲,是 2112 个逻辑单元)

注意:第一次导入工程,必须执行上图的方式来转换。后续编写和修改 CPLD 代码后,可以仍然执行上图方式,也可以直接点下图的“编译”按钮来编译。

执行到这里,会在 logic 下生成 vo 文件出来(在 \logic\simulation\modelsim 下)。

如果用户有自己的逻辑,可以在这时,把逻辑加入到 analog_ip.v 中去,并点上图的三角号重新编译。

到这里,Quartus 工具的任务也完成了。

接下来打开 Supra 来继续生成 bin 文件。

4. Supra 下编译出最终的 bin

在 Supra 工具里,打开该工程(\example\logic\)。

然后,点击【左上角 Tool】->【Compile】,在弹出的画面中点右下角的【run】。

编译成功后,画面会有提示。

plaintext
Compile design example_board done with code 0

然后在 logic 路径下可以看到新编译出来的 bin。

这个 bin 就是要烧录到芯片的 cpld.bin。

到这里,Supra 工具的任务完成。

至于烧录,重新回到 VSCode 下烧录 logic 即可。如下图:

到这里,新建一个空工程、转换、编译、烧录的整个流程描述完毕。

强调:

  • 后续如果在 VE 里修改过配置,则需要走一个全过程:VSCode 下 prepare LOGIC 再生成一遍 CPLD 模板、合并 analog_ip_tmpl.v 到 analog_ip.v 中去、启动 Quartus 去转换、Supra 下编译,回到 VSCode 下烧录 logic。
  • 后续如果只是在 Quartus 下编写 CPLD 代码,需要走的几步:Quartus 下编译、Supra 下编译、回到 VSCode 下烧录 logic。

五、上述过程中的说明

上述流程中涉及两个比较关键的点:

  1. 自定义模块的命名

    自定义的逻辑,自定义文件名必须与自定义模块名相同,就是在 platformio.ini 中设置的 ip_name 的名字。

    这个对应关系,在上述流程点 prepare LOGIC 自动生成代码时,会自动完成。

    如果是手工编辑的逻辑代码,或者对这里的命名进行过改动,会出现后续 Quartus 中使用的异常。

  2. VE 中定义的信号关联

    在 AG32 中,MCU 和 CPLD 和外部引脚,三者是相互独立的。

    • MCU 用的 IO,在 VE 里,可以关联到外部引脚 Pin_xx;
    • CPLD 用的 IO,在 VE 里,可以关联到外部引脚 Pin_xx;
    • MCU 的某一路信号又可以直接和 CPLD 的某一路信号,在 VE 里,关联起来;

    所以,VE 是很关键的一个桥梁。

    在 VE 中定义好以后,运行 prepare LOGIC 会自动产生 CPLD 的顶层模块的输入输出接口,这些接口就是 CPLD 和 MCU 与外部引脚关联的信号通路。

    这里着重描述下 3 种情况在 VE 文件里的定义。

A. MCU 和外部引脚的关联

比如,定义 GPIO 到外部引脚:GPIO4_3 PIN_32

比如,定义串口 0 到外部引脚:UART0_UARTRXD PIN_31

定义格式为:MCU 的 FunctionName + 空格 + Pin 脚 ID。

这部分在 MCU 使用里描述的很多了,不再赘述。

B. CPLD 和外部引脚的关联

比如,定义 LED 到外部引脚:LED_D3 PIN_32:OUTPUT

定义格式为:CPLD 信号名称 + 空格 + Pin 脚 ID:方向

其中,

  • CPLD 信号名称,是自定义名称,这个名称随后可以在 CPLD 中引用;
  • 方向,有 3 种:OUTPUT、INPUT 和 INOUT(这个方向是 CPLD 对外部引脚来说的)。
  • 方向是可选的,可加可不加。如果不加,则是默认的 INOUT(双向)。

上述定义在 VE 里添加后,执行 prepare LOGIC 命令,在自动生成的 CPLD 工程中,可以看到输出到 CPLD 顶层模块接口中的 item 如下:

verilog
input BTN_L1;
output LED_D2;
output LED_D3;

那么,

  • input BTN_L1: 是 Pin 到 CPLD 的信号;
  • output LED_D2: 是 CPLD 到 Pin 的信号;
  • output LED_D3: 是 CPLD 到 Pin 的信号;

然后在 CPLD 代码中操作 LED_D3 这个信号的高低,最终操作的 PIN_32 管脚的高低。

(注:VE 里每行最后定义的方向是可选的)

C. MCU 和 CPLD 之间的关联

比如,定义 GPIO 信号到 CPLD:GPIO4_1 iocvt_chn

比如,定义串口 1 的 TX 信号到 CPLD:UART1_UARTTXD txd_chn

verilog
GPIO4_3 iocvt_chn;
UART1_UARTTXD txd_chn;

定义格式为:MCU 的 FunctionName + 空格 + CPLD 信号名称

其中,

  • 这里的 FunctionName,同 1 中的 FunctionName,就是 MCU 里的通路定义。更多定义参考《AGRV2K_逻辑设置.pdf》,里边有全部的 MCU 端可用的 FunctionName 列表。
  • 除了 MCU 的 FunctionName(映射到引脚)外,MCU 对 CPLD 还开放出更多的内核级通路接口,如:mem_ahb_各通道,dma 各通道,MCU 的 reset 和 stop 等信号,具体定义也参考《AGRV2K_逻辑设置.pdf》。

这里定义后,执行 prepare LOGIC 命令,在自动生成的 CPLD 工程中,可以看到输出到 CPLD 顶层模块接口的 item 如下:

verilog
output iocvt_chn_in;
input iocvt_chn_out_data;
input iocvt_chn_out_en;
input txd_chn_out_data;
input txd_chn_out_en;

对于 iocvt_chn 来说,对应的是没有指定方向的普通 GPIO(GPIO4_3)口,则这里生成的信号同时包含有输入和输出两种。

注:这里的输入和输出,是相对于 CPLD 端来说的。蓝色的 input/output,就是该信号的方向。比如:

  • output iocvt_chn_in:是 CPLD 输出到 MCU 的信号;
  • input iocvt_chn_out_data:是 MCU 输出到 CPLD 的信号;
  • input iocvt_chn_out_en:是 MCU 输出到 CPLD 的信号(en 信号很少用,一般可忽略);

对于 txd_chn 来说,由于 MCU 的串口 TX 是定义好方向的,是 MCU 的 output 方向。所以在 CPLD 里只有两个 item 项:

  • input txd_chn_out_data:是 MCU 输出到 CPLD 的信号;
  • input txd_chn_out_en:是 MCU 输出到 CPLD 的信号(en 信号很少用,一般可忽略);

如果对普通 GPIO 也设置了方向,则导出到 CPLD 的方向也就只有对应方向的信号了。

如:VE 里设置 GPIO4_1 iocvt_chn 为 MCU 的输出:

plaintext
GPIO4_1 iocvt_chn:OUTPUT

则 prepare LOGIC 后生成的 item 只有 input 的两项。如下:

verilog
input iocvt_chn