4. 架构设计:存算一体芯片的宏观架构、处理单元阵列、数据流设计

好,咱们进入第四章。这一章我打算聊聊存算一体芯片的骨架——架构设计。

你想想看,光有计算单元没有数据通路,那就是一堆废铁。光有存储没有计算,那也就是个内存条。存算一体的精髓,就是把这两者揉在一起,让数据在存储的地方就地计算。说白了,就是让数据少跑路,计算多干活。

4.1 宏观架构:从冯·诺依曼到存算一体

传统的冯·诺依曼架构,CPU和内存是分开的。数据得从内存搬到CPU,算完再搬回去。这个搬运过程,就是著名的“存储墙”瓶颈。我早年做AI加速器的时候,经常发现芯片算力堆上去了,但实际性能上不去。为什么?数据搬运堵死了。

存算一体芯片的宏观架构,核心思路就是打破这堵墙。我个人习惯把宏观架构分成三类:

  • 近存计算(Near-Memory Computing):计算单元紧挨着存储单元,距离缩短,但物理上还是分开的。
  • 存内计算(In-Memory Computing):计算直接在存储单元内部完成,比如用ReRAM、SRAM阵列做矩阵乘法。
  • 混合架构:把近存和存内结合起来,灵活应对不同场景。

我在项目中遇到过一种典型的混合架构:芯片中央是一个全局控制器,周围分布着多个存算一体Tile(瓦片)。每个Tile内部有自己的存储阵列和计算单元。数据在Tile内部完成大部分计算,只有需要全局同步时才走片内总线。这种设计,说白了就是“分而治之”。

核心要点:宏观架构的关键在于“数据本地化”。数据离计算越近,延迟越低,能效越高。设计时,要优先考虑数据在哪个Tile、哪个阵列上计算,而不是先考虑计算单元怎么摆。

4.2 处理单元阵列:计算的核心引擎

处理单元阵列,就是存算一体芯片的“肌肉”。它由大量处理单元(PE)组成,每个PE内部集成了存储和计算逻辑。

我常用的阵列结构有两种:

  • 脉动阵列(Systolic Array):数据像心跳一样在阵列中流动,每个PE只和相邻PE通信。这种结构非常适合矩阵乘法,Google的TPU就是典型代表。
  • 近邻互联阵列(Mesh Network):每个PE可以和上下左右四个邻居通信,灵活性更高,适合图计算、稀疏计算等不规则任务。

嗯,这里要注意:脉动阵列虽然效率高,但遇到稀疏数据时,很多PE会空转。我曾经在一个稀疏矩阵乘法项目中,脉动阵列的利用率只有30%。后来改用近邻互联阵列,配合数据压缩,利用率提升到了70%以上。

每个PE内部,我习惯这样设计:

// PE内部结构伪代码
module PE (
  input [DATA_WIDTH-1:0] data_in,
  input [DATA_WIDTH-1:0] weight_in,
  output [DATA_WIDTH-1:0] data_out,
  output [DATA_WIDTH-1:0] partial_sum_out
);
  reg [DATA_WIDTH-1:0] local_memory;  // 本地存储权重或数据
  wire [DATA_WIDTH-1:0] product = data_in * weight_in;
  wire [DATA_WIDTH-1:0] sum = product + partial_sum_in;
  // 数据流控制逻辑
  always @(posedge clk) begin
    if (mode == MAC_MODE) begin
      partial_sum_out <= sum;
    end else if (mode == LOAD_MODE) begin
      local_memory <= data_in;
    end
  end
endmodule

这个PE麻雀虽小五脏俱全。它既能做乘加运算,又能本地存数据。你想想看,成千上万个这样的PE连在一起,就是一台强大的存算一体引擎。

个人经验:设计PE阵列时,别光盯着计算单元。数据加载和结果收集的带宽同样重要。我见过不少设计,PE算力堆得很高,但数据喂不进去,结果也拿不出来,整体性能大打折扣。建议在阵列周围预留足够的缓冲区和数据总线。

4.3 数据流设计:让数据高效流动

数据流设计,说白了就是决定数据怎么在阵列中移动。不同的数据流模式,直接影响芯片的性能和能效。

我总结了几种常见的数据流模式:

数据流模式 描述 适用场景 优缺点
权重固定(Weight Stationary) 权重数据留在PE本地,输入数据在阵列中流动 卷积神经网络(权重复用率高) 权重搬运少,但输入数据需要广播
输入固定(Input Stationary) 输入数据留在PE本地,权重数据在阵列中流动 全连接层(输入复用率高) 输入搬运少,但权重需要广播
输出固定(Output Stationary) 部分和结果留在PE本地,输入和权重都在流动 矩阵乘法(减少中间结果搬运) 减少写回次数,但控制逻辑复杂
混合模式(Hybrid) 根据层类型动态切换数据流模式 多类型网络混合部署 灵活性高,但调度器设计复杂

我个人习惯用混合模式。为什么?因为实际部署的神经网络,往往是卷积、全连接、激活函数、池化等混合在一起的。单一数据流模式很难在所有层上都达到最优。我曾经在一个项目中,用权重固定模式跑卷积层,用输入固定模式跑全连接层,整体性能比单一模式提升了40%。

数据流设计还有一个关键点:数据复用。你想想看,如果每个数据只被用一次,那存算一体的优势就大打折扣了。我常用的复用策略包括:

  • 空间复用:同一个数据被多个PE同时使用。比如卷积核的权重,可以广播给所有PE。
  • 时间复用:同一个数据在不同时间被同一个PE使用。比如输入特征图,可以分批次送入PE。
  • 混合复用:空间和时间复用结合,最大化数据利用率。

避坑指南:我曾经在一个项目中,为了追求极致的数据复用,把数据流设计得过于复杂。结果调度器逻辑变得非常庞大,占用了大量芯片面积,功耗也上去了。最后发现,简单一点的数据流,配合合理的缓存设计,反而效果更好。记住:不要过度优化。

4.4 架构设计实战:一个完整的存算一体Tile

说了这么多理论,咱们来点实际的。下面是我设计的一个存算一体Tile的架构图,用SVG绘制。

存算一体Tile架构图 全局控制器 片内数据总线 存储阵列 SRAM / ReRAM 容量: 256KB 位宽: 128bit PE阵列 8x8 脉动阵列 每个PE: 8bit MAC 总算力: 512 GOPS 数据加载通路 中间缓冲区 用于数据重排和缓存 结果收集总线 输出缓冲区 存储部分和或最终结果 片外接口 (DDR / HBM) 控制信号 控制信号

这个Tile的设计思路是这样的:全局控制器负责调度,存储阵列存放权重和输入数据,PE阵列负责计算。数据从存储阵列加载到PE阵列,计算结果通过结果收集总线送到输出缓冲区,最后通过片外接口写回片外DDR。

我实际项目中,一个芯片上集成了16个这样的Tile。每个Tile可以独立工作,也可以协同完成一个大任务。比如做ResNet-50推理时,我把前几层分配给Tile 0-3,中间层分配给Tile 4-11,最后几层分配给Tile 12-15。数据在Tile之间通过片内总线传递,延迟只有几个时钟周期。

小技巧:设计数据流时,建议先用高级语言(比如Python或C++)做行为级仿真,验证数据流是否正确。我习惯用SystemC做周期精确的仿真,虽然慢一点,但能发现很多架构层面的问题。等仿真通过后,再开始RTL编码,这样能避免大量返工。

好了,这一章的内容就到这里。架构设计是存算一体芯片的基石,宏观架构决定了芯片的“骨架”,处理单元阵列决定了“肌肉”,数据流设计决定了“血液”。三者缺一不可。

想要更多资料,联系可可