4. 软核处理器总线接口与系统互联
在 Lattice FPGA 中搭建软核处理器系统,真正决定系统能否稳定工作的往往不是 CPU 内核本身,而是总线接口与互联结构。软核处理器(如 Lattice 的 RISC-V 核、Mico32 或用户自研核)通过片上总线连接指令存储器、数据存储器、外设寄存器与中断控制器。若总线协议、主从角色、地址译码或时序参数设置不当,常见现象是 CPU 能取指但读写外设失败、外设响应错位,或系统在综合后时序无法收敛。本节围绕 Wishbone、AXI、APB 三类典型片上总线,说明它们在 Lattice FPGA 中的实现方式,分析软核的主/从接口,并给出一个基于总线的 LED 与按键外设互联系统设计示例。
一、三类总线在 Lattice FPGA 中的定位
Wishbone 是开放、轻量的同步总线,信号少、握手简单,非常适合软核处理器与片上外设互联。它采用“主设备发起 STB/CYC,从设备以 ACK 应答”的握手方式,读写在同一个时钟域内完成,逻辑资源占用小,是 Lattice FPGA 中软核系统最常用的互联总线。AXI 是面向高性能的突发传输总线,具有独立的读地址、读数据、写地址、写数据、写响应五通道,支持乱序与 outstanding 传输,适合连接 DDR 控制器、高速数据通路等带宽敏感模块;在 Lattice 器件中通常用于较复杂的 SoC 或与硬核 IP 对接。APB 是 AXI 体系中的低带宽外设总线,协议最简单,只有 PSEL、PENABLE、PWRITE、PADDR、PWDATA、PRDATA、PREADY 等信号,适合挂接 UART、GPIO、定时器等慢速外设。
三者的典型分工是:软核处理器通过 Wishbone 或 AXI 作为主接口访问存储器和总线桥;总线桥将高速总线转换为 APB,再连接低速外设。这样既保证 CPU 取指与数据访问的效率,又让外设接口保持简单。需要强调的是,无论使用哪种协议,地址位宽、数据位宽、字节使能和时钟域必须与软核配置一致,否则会出现读写错位。
二、软核处理器的主接口与从接口
软核处理器通常具有两类总线接口。主接口(Master)由 CPU 主动发起读写,用于取指令和访问数据存储器、外设寄存器。以 Wishbone 主接口为例,关键信号包括:wb_cyc_o 表示总线周期有效,wb_stb_o 表示选通,wb_we_o 表示写使能,wb_adr_o 为地址,wb_dat_o 为写数据,wb_sel_o 为字节选择,从设备通过 wb_ack_i 应答。CPU 发出地址后,必须等待 ACK 有效才能结束本次传输;若从设备未及时应答,CPU 会插入等待周期。
从接口(Slave)则出现在存储器与外设一侧。它接收主设备发来的地址、数据和选通信号,在地址命中时驱动读数据并返回 ACK。设计从接口时,地址译码是核心:每个从设备被分配一段唯一地址区间,只有落在该区间内的访问才被响应。例如指令 BRAM 分配 0x0000_0000–0x0000_3FFF,数据 BRAM 分配 0x1000_0000–0x1000_1FFF,GPIO 外设分配 0x8000_0000–0x8000_000F。若两个从设备地址重叠,总线会出现多设备同时驱动,导致读数据冲突。
在 Lattice 工具链中,主从接口通常以 IP 或参数化 HDL 模块形式提供。使用 Radiant 或 Diamond 的 IP Catalog 生成 Wishbone 互联时,需要设置数据位宽(常见 32 位)、地址位宽(常见 30 位或 32 位)、从设备数量与各自基地址。若软核配置为 32 位数据、字节寻址,则地址最低两位不参与译码,译码器应比较高位地址。
三、基于总线的 LED 与按键外设互联系统设计
下面给出一个可综合的互联示例。系统包含:软核 CPU(Wishbone 主接口)、指令 BRAM、数据 BRAM、GPIO 外设(含 LED 输出寄存器与按键输入寄存器)、Wishbone 互联与地址译码。地址分配如下表所示。
| 从设备 | 基地址 | 地址范围 | 数据位宽 | 说明 |
|---|---|---|---|---|
| 指令 BRAM | 0x0000_0000 | 0x0000_0000–0x0000_3FFF | 32 bit | 存放程序,只读为主 |
| 数据 BRAM | 0x1000_0000 | 0x1000_0000–0x1000_1FFF | 32 bit | 堆栈与变量 |
| GPIO(LED/按键) | 0x8000_0000 | 0x8000_0000–0x8000_000F | 32 bit | 偏移 0x0 为 LED,0x4 为按键 |
GPIO 从接口的寄存器映射为:偏移 0x0 写 LED 输出寄存器,偏移 0x4 读按键输入寄存器。按键输入需要同步与消抖,建议在 GPIO 内部用两级触发器同步按键信号,再用计数器做约 10 ms 消抖(若时钟为 50 MHz,计数阈值约 500000)。LED 输出寄存器直接驱动引脚,注意 Lattice FPGA 的 IO 电平标准要与开发板一致,例如 3.3 V LVCMOS33。
地址译码逻辑可用组合逻辑实现,核心判断如下:
// 地址译码(示例,32 位地址,字节寻址)
wire sel_bram_i = (wb_adr[31:14] == 18'h00000); // 0x0000_0000–0x0000_3FFF
wire sel_bram_d = (wb_adr[31:13] == 19'h08000); // 0x1000_0000–0x1000_1FFF
wire sel_gpio = (wb_adr[31:4] == 28'h8000000); // 0x8000_0000–0x8000_000F
// 读数据多路选择
always @(*) begin
case (1'b1)
sel_bram_i: wb_dat_i = bram_i_dout;
sel_bram_d: wb_dat_i = bram_d_dout;
sel_gpio: wb_dat_i = gpio_dout;
default: wb_dat_i = 32'h0000_0000;
endcase
end
// ACK 生成:任一从设备命中即应答
assign wb_ack_i = sel_bram_i | sel_bram_d | sel_gpio;
软件侧通过指针访问外设,例如:
#define GPIO_BASE 0x80000000u
#define LED_REG (*(volatile unsigned int *)(GPIO_BASE + 0x0))
#define KEY_REG (*(volatile unsigned int *)(GPIO_BASE + 0x4))
int main(void) {
while (1) {
unsigned int key = KEY_REG & 0x1; // 读取按键
if (key) LED_REG = 0x01; // 按下点亮 LED0
else LED_REG = 0x00;
}
}
验证时,先确认 CPU 能正确取指(观察指令 BRAM 读数据是否与编译结果一致),再通过写 LED 寄存器观察引脚电平变化,最后读取按键寄存器确认输入通路。若 LED 不亮,依次检查:地址译码是否命中、写使能与字节选择是否正确、IO 约束是否分配、按键消抖是否导致读值恒为 0。若读按键始终为全 1 或全 0,应检查同步触发器与消抖计数器是否复位正确。
综上,软核系统的总线互联应围绕“主接口发起、从接口应答、地址译码唯一、时序参数匹配”四个要点展开。Wishbone 适合轻量外设互联,AXI 用于高带宽通道,APB 用于低速外设;在 Lattice FPGA 中通过 IP 或参数化 HDL 实现时,务必核对数据位宽、地址位宽、字节使能与时钟域,才能让 LED、按键等外设稳定工作。