1. SoC架构基础:SoC基本概念、典型架构与总线协议

各位同学,咱们今天聊聊SoC架构的基础。说实话,我刚开始接触SoC时,也觉得这东西挺玄乎的——不就是把一堆东西塞进一个芯片里吗?后来真正做项目才发现,这里面的门道可不少。

1.1 SoC到底是什么?

SoC,全称System on Chip,片上系统。说白了,就是把一个完整的电子系统,包括处理器、内存、外设接口、甚至模拟电路,全部集成到一颗芯片上。

我遇到过不少刚入行的朋友,总把SoC和单片机混为一谈。其实区别挺大的——单片机更像是一个“微型计算机”,而SoC更像是一个“完整的系统”。你想想看,一个典型的SoC里,可能同时跑着Linux和RTOS,管理着几十个外设,处理着音频、视频、网络数据流。

核心要点:SoC不是简单的“大号单片机”,而是异构计算、多总线、多时钟域的复杂系统。

1.2 典型架构:ARM Cortex-M vs Cortex-A

做SoC驱动开发,ARM架构是绕不开的。我个人习惯把ARM Cortex系列分成两大类:

Cortex-M系列:实时控制的好手

Cortex-M系列,比如M0、M3、M4、M7,主打低功耗和实时响应。我最早接触的是STM32F103,用的就是Cortex-M3内核。这类处理器有个特点——没有MMU(内存管理单元),跑不了Linux,但中断响应极快。

嗯,这里要注意:Cortex-M系列虽然不能跑Linux,但FreeRTOS、RT-Thread这些实时系统跑得飞起。我在做工业控制项目时,就喜欢用M4内核的芯片,因为它的DSP指令集处理传感器数据特别顺手。

Cortex-A系列:应用处理器的王者

Cortex-A系列,比如A7、A53、A72,是跑Linux的主力。这类处理器有MMU,支持虚拟内存,可以跑复杂的操作系统。

我曾经在一个项目中,需要在A53上同时跑Linux和裸机程序。怎么做到的?靠的是ARM的异常级别(Exception Levels)机制——EL0到EL3,每个级别有不同权限。Linux跑在EL1,安全监控程序跑在EL3,互不干扰。

特性 Cortex-M Cortex-A
MMU
操作系统 RTOS/裸机 Linux/Android
中断延迟 极低(~12周期) 较高(~100周期)
典型应用 电机控制、传感器 手机、路由器

1.3 总线协议:AMBA家族

SoC里这么多模块,怎么连起来?靠总线。ARM公司搞了一套AMBA(Advanced Microcontroller Bus Architecture)协议,现在基本是行业标准。

AXI:高性能总线

AXI(Advanced eXtensible Interface)是AMBA家族的老大哥。它支持乱序传输、突发传输,带宽高得吓人。我调试过的一个视频处理SoC,CPU和GPU之间就是通过AXI总线通信的,跑在400MHz,64位宽,理论带宽3.2GB/s。

为什么AXI这么快?因为它把地址、数据、控制信号分开了,还支持多个未完成的事务同时进行。说白了,就是流水线做得深,效率高。

个人经验:写AXI驱动时,一定要注意outstanding transaction的数量。我曾经设得太高,结果DDR控制器撑不住,数据全乱了。后来改成8个,稳得很。

APB:简单外设的专属通道

APB(Advanced Peripheral Bus)就简单多了。它不支持流水线,不支持突发传输,但胜在逻辑简单、功耗低。SoC里那些慢速外设,比如UART、GPIO、I2C,一般都挂在APB上。

我习惯把APB比作“小区里的慢车道”——虽然慢,但够用,还省电。AXI则是“高速公路”,专门给CPU、DMA、GPU这些“快车”用。

AHB:曾经的王者

AHB(Advanced High-performance Bus)是AXI的前身。现在新设计基本都用AXI了,但很多老芯片还在用AHB。如果你维护的是老项目,AHB还是得懂。

AHB和AXI最大的区别是什么?AHB一次只能处理一个事务,而AXI可以同时处理多个。你想想看,这差距有多大。

1.4 总线矩阵与互联

一个复杂的SoC,不可能只有一条总线。通常会有多个AXI、AHB、APB总线,通过总线矩阵(Bus Matrix)或互联(Interconnect)连在一起。

我参与过的一个项目,用的是ARM的NIC-400互联。它就像一个智能交换机,能把CPU的访问请求路由到正确的内存或外设。这里有个坑——总线矩阵的仲裁策略会影响性能。如果CPU和DMA同时访问DDR,谁优先?

避坑指南:我曾经遇到过一个问题:DMA从摄像头采集数据,CPU同时要读取同一块内存。结果总线矩阵的仲裁策略设成了“CPU优先”,导致DMA丢帧。后来改成“轮询模式”,问题解决。

1.5 时钟域与复位

SoC里不同模块可能跑在不同频率下。比如CPU跑1GHz,APB外设跑50MHz。这就涉及跨时钟域(CDC)的问题。

我刚开始做驱动时,总觉得时钟域是硬件工程师的事。直到有一次,我在APB总线上读一个寄存器,读回来的值忽大忽小。查了半天,原来是CPU时钟域的信号没做同步,直接送到了APB时钟域。

解决办法?用两级同步器(2-flop synchronizer)。代码很简单:

// 跨时钟域同步,从clk_a到clk_b
reg sync_1, sync_2;
always @(posedge clk_b) begin
    sync_1 <= signal_from_clk_a;
    sync_2 <= sync_1;
end
assign synced_signal = sync_2;

嗯,这里要注意:两级同步器只能处理单比特信号。如果是多比特数据,得用异步FIFO或者握手协议。

1.6 小结

今天咱们聊了SoC的基本概念、ARM Cortex-M/A的区别、AMBA总线协议,还有时钟域和总线矩阵。这些东西是驱动开发的基础,就像盖房子要先打地基一样。

下一章,我会带大家深入Linux内核的驱动框架,看看怎么给这些硬件模块写驱动。到时候,咱们会用到今天讲的总线地址映射、中断处理这些知识。

记住一句话:理解硬件,才能写好驱动。别急着写代码,先把SoC的架构吃透。