大量的SOC系统都是FPGA挂在MCU的APB总线或AHB总线上进行通讯,本文是基于APB2总线的片上SOC系统讲解。
1. 引言:当SoC遇见APB2总线
1.1 SoC的核心价值
SoC(System on Chip)通过集成CPU、存储器、外设接口等模块于单芯片,实现了性能、功耗与体积的完美平衡。在智能家居(如语音控制设备)和工业控制(如PLC控制器)中,SoC通过APB总线管理低速外设(如UART、SPI),而AHB总线则处理高速数据流(如DMA传输)。通俗一点说SOC(片上系统)就是一个自带MCU的FPGA,其中FPGA与MCU的通讯部分已经封装好了。
1.2 APB总线层级定位
在AMBA总线家族中,APB2作为低速外设专用总线,通过总线桥与AHB/AXI互联,形成三级架构:
- AHB:连接CPU、DMA等高带宽模块(如视频编解码器)。
- APB:挂接GPIO、定时器等低功耗外设(如温度传感器)、
这种分层设计既降低了系统复杂度,又实现了功耗优化(APB功耗仅为AHB的1/5)。
某国产SOC片内结构
2、结构解析
一般的SOC结构包含MCU内核通常是M3、M4等,本例使用的是M3(官网查看对应产品的结构图等)
对于用户来说,与操作标准库步骤大致一致,只不过需要写两份代码,一份对应MCU,一份对应FPGA。下面是MCU部分:
官方会给出各个子模块的内存映射也就是我们常说的地址,这些功能都可以通过FPGA来实现,但是注意,此时功能的实现端再MCU,FPGA仅负责IO功能。
总线地址如下:
需要注意的是,在MCU端,是不需要配置APB2总线的相关设置的,只需要知道总线基地址,按顺序为结构体赋值就行了,因此没有paddr,pwrite,pwdata,psel1等寄存器,他们都是由M3内核自动分配,配置到FPGA中,只有在FPGA中才需要对上述的总线信号进行处理。
根据官网下载对应的库函数,添加。
注意重点来了:自己新建功能时要注意对应的总线地址,下面时一个加法器的demo(功能实现在FPGA,数据在MCU)下面时.c与.h文件。
#ifndef MULTIPLE_H
#define MULTIPLE_H
/* Includes ------------------------------------------------------------------*/
#include "gw1ns4c.h"
/* Definitions ------------------------------------------------------------------*/
//type definition
typedef struct
{
__IO uint32_t MULTIPLIER; /* Offset: 0x000 (R/W) [7:0] */
__IO uint32_t MULTIPLICAND; /* Offset: 0x004 (R/W) [7:0] */
__IO uint32_t CMD; /* Offset: 0x008 (R/W) [1:0] */
__I uint32_t RESULT; /* Offset: 0x00C (R/ ) [15:0] */
}MULTIPLE_TypeDef;
//base address
#define MULTIPLE_BASE (APB2PERIPH_BASE + 0x400)
//mapping
#define MULTIPLE ((MULTIPLE_TypeDef *) MULTIPLE_BASE)
//bit definition
#define MUL_MULTIPLIER ((uint32_t) 0x000000FF)
#define MUL_MULTIPLICAND ((uint32_t) 0x000000FF)
#define CMD_START ((uint32_t) 0x00000001)
#define STATUS_FINISHED ((uint32_t) 0x00000010)
#define MUL_RESULT ((uint32_t) 0x0000FFFF)
typedef enum
{
FINISHED_STATUS = 0x0,
NO_FINISHED_STATUS = 0x1
}STATUS;
/* Declarations ------------------------------------------------------------------*/
void setMultiplier(uint32_t multi);
uint32_t getMultiplier(void);
void setMultiplicand(uint32_t multi);
uint32_t getMultiplicand(void);
uint32_t getMultipleResult(void);
void startMultiple(void);
void finishMultiple(void);
STATUS getFinishStatus(void);
uint32_t getMultipleCmd(void);
#endif
#include "multiple.h"
/* Functions ------------------------------------------------------------------*/
void setMultiplier(uint32_t multi)
{
MULTIPLE->MULTIPLIER = multi & MUL_MULTIPLIER;
}
uint32_t getMultiplier(void)
{
return MULTIPLE->MULTIPLIER & MUL_MULTIPLIER;
}
void setMultiplicand(uint32_t multi)
{
MULTIPLE->MULTIPLICAND = multi & MUL_MULTIPLICAND;
}
uint32_t getMultiplicand(void)
{
return MULTIPLE->MULTIPLICAND & MUL_MULTIPLICAND;
}
uint32_t getMultipleResult(void)
{
return MULTIPLE->RESULT & MUL_RESULT;
}
void startMultiple(void)
{
MULTIPLE->CMD |= CMD_START;
}
void finishMultiple(void)
{
MULTIPLE->CMD = 0;
}
uint32_t getMultipleCmd(void)
{
return MULTIPLE->CMD;
}
STATUS getFinishStatus(void)
{
if(((MULTIPLE->CMD&STATUS_FINISHED)>>1))
{
return FINISHED_STATUS;
}
else
{
return NO_FINISHED_STATUS;
}
}
主函数只需要调用即可,此处不过多赘述。
FPGA部分较为简单,选择对应的总线的IP核调用即可,只需要注意对应的信号功能进行编程:
下面是对各个信号的解释:
• master_pclk: APB主控器的时钟信号。所有在APB上的操作都与时钟信号同步。
• master_rst: 复位信号。当此信号激活时(通常为高电平或低电平,取决于具体设计),系统会复位到初始状态。
• master_penable: 使能信号。这个信号表明当前地址和控制信号是有效的。在传输周期中,它会在第二个PCLK上升沿被置位以指示从设备进行数据传输。
• master_paddr[7:0]: 地址总线。指定了要访问的外设寄存器的地址。宽度为8位,意味着它可以访问256个不同的地址位置。
• master_pwrite: 读写控制信号。如果该信号为高电平,则表示执行写操作;如果为低电平,则表示执行读操作。
• master_pwdata[31:0]: 写数据总线。这是在写周期期间发送到外设的数据。宽度为32位。
• master_pstrb[3:0]: 字节选通信号。用来标识master_pwdata中的哪些字节是有效的。每个位对应于master_pwdata中的一个字节(4个字节总共)。例如,如果master_pstrb为4'b1100,那么只有master_pwdata的最高有效两个字节被认为是有效的。
• master_pprot[2:0]: 保护控制信号。提供了关于传输的安全性和缓冲需求的信息。
• master_psel1: 器件选择信号。表明特定的外设已被选中进行通信。这里的“1”可能表示这是第一个外设或者特定编号的外设。
• master_prdata1[31:0]: 读数据总线。这是在外设响应读请求时返回的数据。宽度为32位。
• master_pready1: 就绪信号。由外设发出,表示它已经完成了当前传输并且可以接受下一个传输。这允许外设有时间处理请求而不会丢失数据。
• psel_valid_es1: 这似乎是一个自定义信号名称,可能是用来验证master_psel1是否有效,或者是外部逻辑的一部分,用于控制或监测master_psel1的状态。
3、功能验证
FPGA配置了乘法器,MCU通过APB2总线传输乘数和被乘数,FPGA计算结果以后再通过APB2总线返回结果。
配置了乘数和被乘数分别为20和40。FPGA计算以后把结果传回MCU,再通过串口打印出来。由图可见,结果正确,功能正常。