文章目录[隐藏]
本文档详细介绍了AG32开发中MCU与CPLD交互的具体方式、技术原理以及工程实践。作为工程师,理解并掌握这些交互机制对于充分发挥AG32平台的性能优势至关重要。 CPLD工程创建及编译的操作流程,可参考文档《AG32下fpga和cpld的使用入门》。在工程中,用户逻辑部分编写从analog_ip.v的接口开始。 MCU和CPLD之间的交互可以分为以下几种方式: 重要说明:不建议CPLD作为主设备对MCU写入。在MCU和CPLD交互中,CPLD更像一个外设,这种设计模式确保了系统的稳定性和可控性。 基础配置:在VE中定义信号: 这表示用MCU的GPIO(gpio4_1)来输入信号到CPLD。 代码实现:Prepare LOGIC工程后,可以看到analog_ip.v接口中的信号: 这里的 工程实践考量: 对于来自MCU的异步信号,CPLD内部应至少使用两级触发器进行同步处理,以避免亚稳态: 关键设计要点: 除了GPIO信号,PWM输出信号等也可以输入到CPLD。具体样例可参考"logic样例3.mcu信号到cpld到pin"。 基础配置:在VE中定义信号: 代码实现: 当CPLD中控制 进阶中断处理设计: 实践考量: 在地址设计中,CPLD的地址区间是: 当MCU对这个区间内的地址访问时,相当于访问了CPLD的"寄存器"。MCU端的操作非常简单: 当MCU读写动作发生时,AHB总线会将动作拆解为读写信号,传递到analog_ip.v的接口。理解AHB总线协议是设计CPLD从设备的关键。 关键信号详解: 传输类型详解: 以下是一个完整的CPLD AHB从设备实现示例: 原文提供的基础读写示例: MCU读操作的CPLD响应: MCU写操作的CPLD响应: 对于串口、SPI、I2C等慢速设备,直接挂载到AHB总线效率较低且设计复杂。需要通过AHB到APB的桥接(ahb2apb.v模块)来实现。 ahb2apb.v模块功能: APB关键信号: APB读操作实现: APB写操作实现: 完整的APB从设备实现: DMA实现的基本逻辑: 对于CPLD来说,MCU来读取数据和DMA来读取数据是一致的,DMA读取时只是每次读完后会多给CPLD一个clear信号。 DMA触发逻辑的完整实现: 设计原则: 调试技巧: 本文档涉及的完整代码示例可参考以下样例工程: MCU与CPLD的交互是AG32平台的核心优势之一,它结合了MCU的灵活性和CPLD的并行处理能力。通过深入理解AHB/APB总线协议、掌握CPLD内部寄存器映射和逻辑设计,以及熟悉相关的开发工具和调试方法,工程师可以充分发挥AG32平台的潜力,开发出高性能、高可靠性的嵌入式系统。 关键要点回顾: 通过本文档的学习和实践,工程师将能够熟练运用AG32平台进行复杂嵌入式系统的开发。引言
一、MCU与CPLD交互方式概述
二、MCU与CPLD直接信号交互:基础与进阶
2.1 MCU传递信号给CPLD:基础实现
GPIO4_1
iocvt_chn:OUTPUT
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
会对应变化。// 同步器设计
reg iocvt_chn_out_data_sync1;
reg iocvt_chn_out_data_sync2;
always @(posedge sys_clock) begin
iocvt_chn_out_data_sync1 <= iocvt_chn_out_data;
iocvt_chn_out_data_sync2 <= iocvt_chn_out_data_sync1;
end
// 在CPLD内部逻辑中使用 iocvt_chn_out_data_sync2
2.2 CPLD传递信号给MCU:中断与事件处理
GPIO4_2
iocvt_chn:INPUT
output iocvt_chn_in_data,
iocvt_chn_in_data
信号高低时,MCU中的gpio4_2对应变化,可以触发MCU中断。// 中断信号生成逻辑
reg interrupt_pending;
reg [15:0] interrupt_counter;
always @(posedge sys_clock or negedge reset_n) begin
if (!reset_n) begin
interrupt_pending <= 1'b0;
interrupt_counter <= 16'h0;
end else begin
// 基于特定条件生成中断
if (data_ready && !interrupt_pending) begin
interrupt_pending <= 1'b1;
interrupt_counter <= 16'hFFFF; // 中断持续时间
end else if (interrupt_pending && interrupt_counter > 0) begin
interrupt_counter <= interrupt_counter - 1;
end else if (interrupt_counter == 0) begin
interrupt_pending <= 1'b0;
end
end
end
assign iocvt_chn_in_data = interrupt_pending;
三、MCU通过AHB总线读写CPLD:深度技术解析
3.1 AHB总线基础与地址映射
0x60000000 ~ 0x7FFFFFFF
// 读CPLD
int cpRdReg = *((int *)0x60000000);
// 写CPLD
*((int *)0x60000004) = cpWtReg;
3.2 AHB总线协议深度解析
// AHB从设备接口信号
input Ahb_hclk; // AHB时钟
input Ahb_hresetn; // AHB复位,低有效
input [31:0] mem_ahb_haddr; // 地址总线
input [31:0] mem_ahb_hwdata; // 写数据总线
input mem_ahb_hwrite; // 写/读控制 (1=写, 0=读)
input [1:0] mem_ahb_htrans; // 传输类型
input mem_ahb_hready; // 主设备准备好
output [31:0] mem_ahb_hrdata; // 读数据总线
output mem_ahb_hreadyout; // 从设备准备好
output [1:0] mem_ahb_hresp; // 响应信号
2'b00: IDLE
- 空闲状态2'b01: BUSY
- 忙状态2'b10: NONSEQ
- 非连续传输,新传输的开始2'b11: SEQ
- 连续传输3.3 CPLD内部寄存器映射的完整实现
// CPLD内部寄存器定义
reg [31:0] reg_control; // 控制寄存器 (0x00偏移)
reg [31:0] reg_data; // 数据寄存器 (0x04偏移)
reg [31:0] reg_status; // 状态寄存器 (0x08偏移, 只读)
// AHB响应信号
reg hreadyout_reg;
reg [31:0] hrdata_mux;
// 地址解码与寄存器操作逻辑
always @(posedge Ahb_hclk or negedge Ahb_hresetn) begin
if (!Ahb_hresetn) begin
reg_control <= 32'h0;
reg_data <= 32'h0;
reg_status <= 32'h0;
hreadyout_reg <= 1'b1;
end else begin
hreadyout_reg <= 1'b1; // 默认准备好
// 有效传输检测
if (mem_ahb_hready && (mem_ahb_htrans == 2'b10 || mem_ahb_htrans == 2'b11)) begin
if (mem_ahb_hwrite) begin
// 写操作
case (mem_ahb_haddr[23:2])
22'h0: reg_control <= mem_ahb_hwdata;
22'h1: reg_data <= mem_ahb_hwdata;
default: ; // 无效地址忽略
endcase
end else begin
// 读操作 - 准备读数据
case (mem_ahb_haddr[23:2])
22'h0: hrdata_mux <= reg_control;
22'h1: hrdata_mux <= reg_data;
22'h2: hrdata_mux <= reg_status;
default: hrdata_mux <= 32'hDEADBEEF; // 错误标识
endcase
end
end
// 状态寄存器更新逻辑(示例)
reg_status[0] <= data_ready_flag;
reg_status[1] <= error_flag;
// ... 其他状态位
end
end
assign mem_ahb_hrdata = hrdata_mux;
assign mem_ahb_hreadyout = hreadyout_reg;
assign mem_ahb_hresp = 2'b00; // OKAY响应
// MCU端:int value = *((int *)0x60000004);
reg [31:0] hrdata_reg;
always @(posedge sys_clock) begin
if (mem_ahb_htrans == 2'b10 && // NONSEQ状态
mem_ahb_hready && // master已ready
!mem_ahb_hwrite && // 读操作
mem_ahb_haddr[23:0] == 'h04) // 地址匹配
begin
hrdata_reg <= hwdata_reg; // 准备读数据
end
end
assign mem_ahb_hrdata = hrdata_reg;
// MCU端:*((int *)0x60000000) = value;
reg [31:0] hwdata_reg;
always @(posedge sys_clock) begin
if (mem_ahb_htrans == 2'b00 && // IDLE状态
mem_ahb_hreadyout && // CPLD已ready
mem_ahb_hwrite && // 写操作
mem_ahb_haddr[23:0] == 'h00) // 地址匹配
begin
hwdata_reg <= mem_ahb_hwdata; // 接收数据
end
end
四、MCU通过AHB转APB的数据交互:慢速外设桥接
4.1 AHB2APB桥接原理
4.2 APB总线协议与信号详解
input apb_clock; // APB总线时钟
input apb_resetn; // APB复位信号
input apb_psel; // 外设选择信号
input apb_penable; // 传输使能(第二周期)
input apb_pwrite; // 传输方向 (1=写, 0=读)
input [31:0] apb_paddr; // 地址总线
input [31:0] apb_pwdata; // 写数据总线
output [31:0] apb_prdata; // 读数据总线
4.3 APB从设备的完整实现
// MCU端:int value = *((int *)0x60000004);
reg [31:0] ardata_reg;
always @(posedge apb_clock) begin
if (!apb_pwrite && // 读操作
apb_penable && // 第二周期
apb_psel && // 片选有效
apb_paddr[11:0] == 'h04) // 地址匹配
begin
ardata_reg <= awdata_reg; // 准备读数据
end
end
assign apb_prdata = ardata_reg;
// MCU端:*((int *)0x60000000) = value;
reg [31:0] awdata_reg;
always @(posedge apb_clock) begin
if (apb_pwrite && // 写操作
apb_penable && // 第二周期
apb_psel && // 片选有效
apb_paddr[11:0] == 'h00) // 地址匹配
begin
awdata_reg <= apb_pwdata; // 接收数据
end
end
// CPLD内部寄存器
reg [31:0] apb_reg_config; // 配置寄存器
reg [31:0] apb_reg_data; // 数据寄存器
reg [31:0] apb_reg_status; // 状态寄存器
always @(posedge apb_clock or negedge apb_resetn) begin
if (!apb_resetn) begin
apb_reg_config <= 32'h0;
apb_reg_data <= 32'h0;
apb_reg_status <= 32'h0;
end else begin
if (apb_psel && apb_penable) begin
if (apb_pwrite) begin
// 写操作
case (apb_paddr[11:2])
10'h00: apb_reg_config <= apb_pwdata;
10'h01: apb_reg_data <= apb_pwdata;
default: ; // 无效地址忽略
endcase
end
end
// 状态寄存器更新
apb_reg_status[0] <= data_ready;
apb_reg_status[1] <= error_flag;
end
end
// APB读数据输出
assign apb_prdata = (apb_psel && apb_penable && !apb_pwrite) ?
(apb_paddr[11:2] == 10'h00 ? apb_reg_config :
apb_paddr[11:2] == 10'h01 ? apb_reg_data :
apb_paddr[11:2] == 10'h02 ? apb_reg_status : 32'hFFFFFFFF) : 32'h0;
五、DMA在CPLD中的应用:高效数据传输
5.1 DMA工作流程
5.2 CPLD端DMA实现
// DMA相关信号
input dma_clear_signal; // 来自MCU的DMA清除信号
output dma_request_signal; // 输出给MCU的DMA请求信号
// CPLD内部数据缓冲区
reg [31:0] data_buffer [0:63]; // 64个32位数据缓冲区
reg [5:0] write_ptr; // 写指针
reg [5:0] read_ptr; // 读指针
reg data_ready_flag; // 数据准备就绪标志
reg dma_request_reg; // DMA请求寄存器
// 数据写入逻辑(示例:来自外部数据源)
always @(posedge sys_clock) begin
if (external_data_valid) begin
data_buffer[write_ptr] <= external_data;
write_ptr <= write_ptr + 1;
// 缓冲区满时标记数据就绪
if (write_ptr == 6'd63) begin
data_ready_flag <= 1'b1;
write_ptr <= 6'd0; // 环形缓冲区
end
end
end
// DMA请求生成逻辑
always @(posedge sys_clock or negedge dma_clear_signal) begin
if (!dma_clear_signal) begin
// DMA完成,清除请求
data_ready_flag <= 1'b0;
read_ptr <= 6'd0;
dma_request_reg <= 1'b0;
end else if (data_ready_flag && !dma_request_reg) begin
// 数据准备好且未发起DMA请求
dma_request_reg <= 1'b1;
end
end
assign dma_request_signal = dma_request_reg;
// 响应DMA读取(通过AHB接口)
always @(posedge Ahb_hclk) begin
if (mem_ahb_hready && !mem_ahb_hwrite &&
(mem_ahb_htrans == 2'b10 || mem_ahb_htrans == 2'b11)) begin
// DMA读取时更新读指针
read_ptr <= read_ptr + 1;
end
end
// DMA数据输出
assign mem_ahb_hrdata = data_buffer[mem_ahb_haddr[7:2]];
5.3 MCU端DMA配置示例
// MCU端DMA配置示例
void setup_dma_from_cpld(void) {
// 配置DMA通道
DMA_Channel->CPAR = 0x60000000; // CPLD数据地址
DMA_Channel->CMAR = (uint32_t)dma_buffer; // 目标内存地址
DMA_Channel->CNDTR = 64; // 传输数据量
// 配置DMA控制寄存器
DMA_Channel->CCR = DMA_CCR_EN | // 使能DMA
DMA_CCR_MINC | // 内存地址递增
DMA_CCR_PSIZE_1 | // 外设32位
DMA_CCR_MSIZE_1 | // 内存32位
DMA_CCR_TCIE; // 传输完成中断
}
六、AG32 CPLD开发流程与工具链
6.1 开发环境与工具
6.2 开发最佳实践
七、实例工程参考
总结