跳到内容
llama2.c · Communication Lens
记忆模型容量与干扰信号链Lane1 / Lane0Trace 证据取舍完整地图源文件
COMMUNICATION ENGINEER EDITION · ASSOCIATIVE MEMORY → TRANSFORMER LAYER
Transformer 不是神秘黑盒,
而是一台抗干扰的联想存储器
通信工程师可以这样看:key 是扩频码,value 是符号,query 是接收信号;q·k 是相关/匹配滤波,其他 key 的串扰就是多址干扰。Softmax 是干扰抑制与归一化,capacity 是能同时记住多少个 key-value 而不被串扰淹没。
KEYdim 64 · 8 heads · head_size 8QUERYx[64] Q12.20MEMORYKV cache 5 × 512 × 4 × 8LANE 1transformer_layer · 静态码本LANE 0transformer_layer_runtime · 时分复用OUTx[64]
从“记忆”开始 →直接看完整地图
用通信语言重述 build 顺序:
- 先有key / value的叠加记忆
- 用相关器检索:q·k
- 其他 key 造成多址干扰
- 非线性 / softmax压制干扰
- 8 heads = 多条接收支路,4 KV heads = 共享码本组
- 5 层 = 5 套码本,runtime 用TDM复用一套接收机
- 容量提升不是免费:相关器、LUT、控制、面积都要付成本
01 / STORE → QUERY
先存得下,才查得出
线性层先被看成外积记忆:每个训练样本把 value 写到 key 的方向上。检索就是拿 query 去和所有 key 做相关。
02 / SIGNAL → INTERFERENCE
干扰先于架构
不是先有 attention 再解释它;而是先有“多个记忆互相串扰”的问题,才有抑制干扰的 MLP、softmax 和容量设计。
03 / CAPACITY → COST
容量是收益,也是成本
线性记忆容量约等于 key 维度;非线性与指数加权能把容量推高,但相关器、exp LUT、reciprocal LUT 和调度都更贵。
04 / MEMORY → RTL
RTL 是相关器 + 归一化器 + TDM
权重 bank 是码本,score 是相关峰,softmax 是干扰归一化,KV cache 是延迟线;layer_idx 是码本选择,runtime 是五套码本共用一个接收机。
这篇新版不复述模块名,而是借用一篇“从记忆推导 Transformer”的文章视角:线性层、MLP、self-attention 都可以看成不同容量的联想存储器。
对通信工程师来说,最有共鸣的不是公式本身,而是这些老概念:相关、多址干扰、处理增益、容量、归一化、时分复用。说明:文章里的 64 → 1.2M 是理想随机向量记忆实验下的容量估计,不等于真实模型记住了多少知识;本页用它作为理解透镜,再落到本项目的 lane1 / lane0 RTL。
00 / MEMORY MODEL
先把 Transformer 看成存储器
一个 key-value 记忆,就是一次外积写入;一次查询,就是一次相关
这篇新版从“机器怎么记住很多事实”出发。线性层、MLP、self-attention 都可以被看成不同容量的联想记忆。对通信工程师,这几乎就是熟悉的码本、相关器和多址干扰问题。
ASSOCIATIVE MEMORY · 最简数学模型
M= Σi vi kiT // 查询 q:拿 query 和所有 key 做相关y= M q = Σi (ki · q) vi // 把它拆成“目标信号 + 其他记忆的干扰” =(kj · q) vj+ Σi≠j (ki · q) vi
第一项是想要的记忆;第二项是其他 key 和 query 的互相关,也就是干扰。容量讨论的就是:存储多少个 key 之后,干扰会强到与目标信号相当。
COMMUNICATION TRANSLATION · 通信工程师的词典
Key = 扩频码 / 签名
key 决定“用哪个方向写入、用哪个相关器读出”。好的 key 之间互相关低,干扰就小。
Value = 发送符号
value 是真正想取回的信息;在硬件里它来自权重行、V cache 或 FFN hidden。
Query = 接收信号
query 和每个 key 做相关;相似度越高,权重越大。其他 key 的相关输出就是多址干扰。
Capacity = 可分辨记忆数
容量不是存储比特数,而是“在干扰淹没信号之前,能同时记住并可靠取回多少个 key-value”。
| 通信概念 | 联想记忆 | 本项目 RTL | 在哪一层 |
|---|---|---|---|
| 扩频码 / 签名 | key k_i | 权重行 / K cache | qkv_weight_bank、attention_score |
| 发送符号 | value v_i | V cache / weight value row | attention_value_sum、ffn |
| 接收信号 / 匹配滤波输入 | query q | x[64] / q vector | x_in_flat、q_out_wire |
| 相关器 / 匹配滤波 | q · k_i | int8_group_dot + fixed_scale_mul | int8_matvec_core、attention_score |
| 多址干扰 MAI | Σ_{i≠j}(k_i·q)v_i | 其他历史 token 的 score 干扰 | attention_score |
| 信号干扰比 SIR | 目标相关 / 干扰相关 | logit 大小、缩放系数 | softmax 前的 score |
| 分集合并 | 按相似度加权求和 | prob × V 累加 | attention_value_sum |
| 归一化 / AGC | 权重归一化 | softmax、RMSNorm | softmax_core、rmsnorm_core |
| 码本选择 | 哪一层记忆 | layer_idx 选择权重 bank | qkv_weight_bank_runtime |
| 时分复用 TDM | 一套接收机处理多套码本 | 五层复用同一 datapath | transformer_layer_runtime |
不要过度映射:文章实验里用 64 维随机 key 得到容量估计;本模型里 dim=64、head_size=8、8 个 head。64 是整个 hidden 维度,不是单个 head 的 key 维度。这里借用的是“相关 + 干扰 + 容量”的直觉,不是把每个数字一一对应。
01 / CAPACITY & INTERFERENCE
为什么一层能记住多少,取决于干扰怎么被压下去
从 64 到约 1.2M:不是魔法,是干扰抑制方式变了
文章用理想随机 key/value 做实验来估计单层能可靠取回多少条记忆。容量数字不是“模型记住多少事实”,而是“在串扰淹没目标前,能放多少个 key-value”。这个视角对通信工程师很自然:容量由相关器、处理增益和干扰抑制能力共同决定。
CAPACITY LADDER · 图示按对数直觉,不是线性比例
外积线性记忆≈ d ≈ 64
最优线性记忆≈ 2d ≈ 128
ReLU 门控≈ 128
Square ReLU / GLU≈ 2,700
Softmax attention≈ 1.2M(理想实验)
数字来自文章中的随机向量记忆实验,用来比较不同结构的“容量量级”,不是 stories260K 模型实际记住的知识量。
INTERFERENCE-TO-SIGNAL · 为什么容量会卡住
// 随机单位 key 时,干扰项像随机游走叠加 ISR ≈ (n − 1) / d // 令 ISR ≈ 1,得到线性记忆的容量 n ≈ d // 如果先把 key 白化 / 消相关,可到 2d n ≈ 2d
通信里的直觉:用户数 n 增加,多址干扰累积;处理增益 d 增加,相关器能把目标从干扰里拉出来。这里的 d 就是 key 空间的维度。
非线性的作用:ReLU / square ReLU / SiLU 把低相似度和负相似度的项压小,相当于软干扰抑制。相似度 0.1 经过平方变成 0.01,目标相似度 1 仍然是 1,于是干扰被压得比信号更快。
Softmax 的作用:把 score 变成指数权重再归一化,强相关路径保留、弱相关路径被指数压小。文章把这看成容量从多项式增长跳到指数增长的关键一步;硬件上对应 exp LUT、求和、reciprocal LUT。
线性相关器
最便宜:一次 dot + scale + accumulate。容量约等于 key 维度,所有干扰项都按相似度原样进入输出。
非线性门控
加一个阈值/平方/门控,把低相似度项压掉。代价是额外乘法、激活函数和更复杂的量化。
Softmax 选择
指数加权 + 归一化,让强相关路径接近 one-hot,弱路径被压到很小;同时保持可微,能训练。
通信类比不是严格证明:“attention 是记忆检索”能帮助建立直觉,但训练动力学、优化和真实知识存储都不能只靠这个类比解释。它是有用的第一层透镜,不是完整理论。
02 / SIGNAL CHAIN
把一层 Transformer 走成通信接收链
query → 相关器 → 干扰抑制 → 分集合并 → 非线性检测
把 self-attention 看成一个接收机:query 是收到的信号,K/V cache 是码本和符号历史,score 是相关输出,softmax 是干扰归一化,value sum 是分集合并。FFN 则是后级的非线性检测/门控。这样看,每个 RTL 模块都有通信语义。
ONE HEAD · 从相关到合并
01 QUERYx / q当前 token 产生的 query 向量信号
02 MEMORYK / V cache历史 token 的 key/value,像延迟线/码本记忆
03 CORRELATORq · k和每个历史 key 做相关,输出 score匹配滤波
04 SCALE× 1/√8head_size 归一化,避免相关值过大处理增益
05 SOFTMAXexp + normalize强相关路径保留,弱相关/干扰被压小干扰抑制
06 COMBINEΣ prob × V按权重合并历史 value,像分集合并合并
07 DETECTWO / FFN投影 + 残差 + SwiGLU 非线性门控检测/门控
COMMUNICATION READING · 为什么这些结构会出现
相关器:每个 head 的 Q 和 K cache 逐项点积,就是在做匹配滤波。目标 key 给出大的相关峰;其他 key 给出较小的互相关,形成多址干扰。
处理增益:1/√head_size把相关值缩放到合适范围,避免 softmax 过早饱和。通信里类似用处理增益/噪声功率做归一化,让判决器工作在线性区。
分组共享:8 个 Q head 只有 4 个 KV head,每 2 个 Q head 共享一套 K/V 码本。这不是“少算一点”而已,而是用码本共享换取存储和带宽。
干扰抑制:softmax 的 exp LUT 和 reciprocal LUT 把弱相关项压小,再做归一化。文章把它看成容量跃升的来源;在 RTL 里它是明确的 LUT + 乘加 + 倒数。
RTL 对应 · 点击定位完整地图
qkv_projection_mem把 query/key/value 从 x 投影出来
attention_scoreq · K 相关器 + scale
softmax_coreexp LUT + sum + reciprocal LUT
attention_value_sumprob × V 分集合并
kv_cache_multi_layer历史码本/延迟线,多 layer bank
ffn_block_memSwiGLU 非线性检测/门控
地图:相关器地图:softmax地图:KV 记忆
03 / LANE 1 → LANE 0
从固定码本到 TDM 码本复用
lane1 是单通道接收机;lane0 是五套码本共用一个接收机
静态层像一个固定在某个频点/码本上的接收机:先证明相关、softmax、合并都算对。runtime 层像 TDM 接收机:同一套相关器和归一化器在五个时间片里切换五套码本。算法没变,变的是码本选择和调度。
Lane 1 · transformer_layer
固定码本接收机。参数LAYER_IDX在 elaboration 时钉死权重文件;端口没有layer_idx。它最适合先做单层验证:输入一个 x,检查相关、softmax、value sum、FFN 是否逐项对齐 golden reference。
Lane 0 · transformer_layer_runtime
TDM 接收机。多一个layer_idx[2:0],每次启动从五套码本 bank 中选一套;KV cache 也按层分成五个 bank。同一套 datapath 依次处理五层,面积换延迟。
| 通信视角 | lane1 静态 | lane0 runtime |
|---|---|---|
| 码本 | 固定一套 _l0 / 对应层 mem | all.mem 五套,layer_idx 选 bank |
| 接收机数量 | 每个静态层一套 datapath | 一套 datapath 分时复用 |
| KV 记忆 | 单层 K/V 延迟线 | 五层 K/V 延迟线,layer × seq 展平 |
| 调度 | 局部 FSM | 局部 FSM + 父级 TDM 循环 |
| 延迟 | 可并行实例化,但面积高 | 五层串行,延迟更长 |
| 面积 | 控制/运算/布线复制 | 一份相关器与归一化器 |
| 代价 | 面积和布线 | TDM 控制、地址加法、LUT 复用 |
layer_idx 的唯一作用 · 把 base address 变成运行时变量
// 权重码本:从固定 row 变成 layer base + row q_addr = (layer_idx × DIM) + row_idx kv_addr = (layer_idx × KV_ROWS) + row_idx // KV 延迟线:五层共用一段大地址空间 flat_write_pos = (write_layer_idx × SEQ_LEN) + write_pos flat_read_pos = (read_layer_idx × SEQ_LEN) + read_pos
这不是新的数学,而是“同一套相关器,运行时换码本”。
父级 TDM · 谁决定下一个时间片
ST_LAYER_WAIT: if (done) { x_work_reg = layer_out_flat if (layer_idx == 4) final_norm() else { layer_idx++; start() } }
父级 forward_core_5layer 负责把上一层输出写回 x,再启动下一层;runtime 层只负责一次 x → x。
地图:lane1 静态地图:lane0 TDM地图:五层循环
通信工程师的复述:lane1 是单码本接收机,先验证解调/合并链路;lane0 是 TDM 多码本接收机,用同一个相关器在五个时隙里处理五层。layer_idx 是时隙/码本选择字,不是新的调制方式。
04 / TRACE EVIDENCE
用信号关系验证通信类比
相关器 → softmax → 合并,不是比喻,是 RTL 里真实连着的线
下面是从当前 RTL 用 trace_signal.py 追出来的信号方向:attention_score 输出 score 进入 softmax;softmax 用 exp LUT 和 reciprocal LUT 生成概率;概率再进入 attention_value_sum。layer_idx 则真实地连接到六个 runtime bank 与多层 KV cache。
EVIDENCE 01 · 相关器 → 干扰抑制
attention_score.scores_flat [output] ├─ transformer_layer_runtime.scores_flat_wire │ └─ softmax_core.score_in_flat │ └─ softmax_core.score_sel └─ transformer_layer.scores_flat_wire └─ softmax_core.score_in_flat
这就是通信链里的“相关器输出 → 判决/归一化器输入”。scores_flat 是每个历史位置的 q·k 相关值,被 softmax_core 当作干扰与目标信号一起处理。
EVIDENCE 02 · softmax 内部的 exp / reciprocal
softmax_core.prob_out_flat ├─ exp_neg_lut_12b.data // 指数查表 ├─ recip_lut_12b.data // 倒数查表 └─ attention_value_sum.probs_flat └─ attention_value_sum.prob_reg
softmax 不是抽象口号:它真的用 exp LUT 把 score 变成指数权重,再用 reciprocal LUT 做归一化;输出概率随即进入 value sum 的分集合并。
EVIDENCE 03 · layer_idx = 码本选择 / TDM 控制
transformer_layer_runtime.layer_idx ├─ rms_weight_bank_runtime.layer_idx ├─ qkv_weight_bank_runtime.layer_idx │ └─ q_addr / kv_addr ├─ kv_cache_multi_layer.write_layer_idx ├─ kv_cache_multi_layer.read_layer_idx ├─ attention_outproj_bank_runtime.layer_idx ├─ ffn_inproj_bank_runtime.layer_idx └─ ffn_outproj_bank_runtime.layer_idx
同一个 layer_idx 同时进入六个 bank 与多层 cache 的地址。这是 lane0 TDM 的硬证据:一套相关器,运行时切换码本。
EVIDENCE 04 · 谁把 x 递回下一个时间片
transformer_layer_runtime.x_in_flat └─ forward_core_5layer.x_work_reg ├─ token_embedding_bank.embed_flat_wire // 第一层 └─ transformer_layer_runtime.out_flat // 后续层 transformer_layer_runtime.done └─ forward_core_5layer.layer_done_wire
接收机算完一个时间片后,done 回到父级;父级把 out 写回 x_work_reg,再切下一个码本。这就是五层 TDM 循环。
REPRODUCE · 本次新增 trace
python C:/Users/PC/Downloads/trace_signal.py attention_score scores_flat --data-only --depth 3 -o lane01_comm_assets/traces/comm_attention_scores.txt rtl python C:/Users/PC/Downloads/trace_signal.py softmax_core prob_out_flat --data-only --depth 3 -o lane01_comm_assets/traces/comm_softmax_probs.txt rtl python C:/Users/PC/Downloads/trace_signal.py transformer_layer_runtime layer_idx --data-only --depth 4 -o lane01_comm_assets/traces/lane0_layer_idx.txt rtl
TRACE FILES
comm_attention_scores.txt · 相关器 → softmaxTRACE ↗comm_softmax_probs.txt · exp / recip / value sumTRACE ↗lane0_layer_idx.txt · TDM 码本 fan-outTRACE ↗lane0_x_in_flat.txt · x_work_reg 循环TRACE ↗lane0_done.txt · done → 父级TRACE ↗
05 / TRADEOFF
容量、算力、面积一起定价
不是“softmax 更高级”,而是它用更多计算换更强的干扰抑制
通信系统里没有免费容量:要更高的处理增益、更强的干扰抑制、更低的误码率,就要更多带宽、算力、存储或延迟。Transformer 也一样。理解每个结构解决了哪个干扰问题,才能理解 RTL 为什么长这样。
1. 写入 key / value先有外积记忆,才有查询对象data
2. 用 query 做相关匹配滤波得到每个 key 的相似度correlator
3. 干扰出现其他 key 的互相关叠加,容量受 ISR 限制problem
4. 非线性门控ReLU / square ReLU / SwiGLU 压低弱相关项MLP
5. 指数加权 + 归一化softmax 把强相关路径选中,把弱干扰压小attention
6. 多头 / GQA多支路相关器;4 个 KV head 共享 K/V 码本heads
7. lane1 静态一层先把单码本接收机验证正确level 3
8. lane0 runtime TDM同一套接收机分时处理五层码本level 4
9. 父级五层循环done → x 写回 → layer_idx++ → 再启动forward_core
容量靠抑制干扰
线性相关器便宜,但容量≈d;非线性与 softmax 用更多计算把弱相关项压下去,容量才跃升。
Softmax 有真实硬件成本
exp LUT、求和、reciprocal LUT、定点归一化都要面积和延迟;它不是一个免费的函数调用。
TDM 省面积,不省时间
runtime 复用一套 datapath,但五层串行;权重 ROM 和 KV cache 容量也没有因为复用而消失。
最重要的结论:不要问“为什么这里要一个 softmax 模块”,而要问“如果不做干扰抑制,容量会掉到哪里”;不要问“为什么 layer_idx 要进 bank”,而要问“同一套相关器怎么在五套码本之间切换”。前者是通信问题,后者是资源调度问题。
06 / FULL MAP
基于 top_full_link_cc_zoom.svg
在完整信号地图里找相关器、记忆和 TDM
地图节点名采用“模块__父级__实例”格式,连线是 AI 注释后的数据流。用下方按钮分别在 lane1、lane0、相关器、softmax、KV memory 和五层循环之间切换。
FOCUS总览lane1 码本lane0 TDM相关器干扰抑制KV 记忆五层循环+-总览
lane0 runtime 相关器/码本选择lane1 静态单层记忆 相关器与 KV 记忆干扰抑制 score / softmax / value combine
滚轮缩放 · 拖拽平移 · 悬停节点看 title 注释 · 按钮切聚焦
先找相关器
qkv_projection_mem、attention_score 是相关器链;它们的输出 scores_flat 直接进入 softmax_core。
再找记忆
kv_cache_multi_layer 是历史 K/V 延迟线;layer_idx 决定读写哪个 layer 的 bank。
最后找 TDM
transformer_layer_runtime 是同一套接收机;forward_core_5layer 用 done 和 layer_idx 驱动五个时间片。
原始可缩放地图:top_full_link_cc_zoom.svgSVG ↗地图数据与连接注释:top_full_link_cc.txtTXT ↗
07 / SOURCES
把新透镜落回可验证文件
文章给视角,项目给实例,trace 给证据
新版不替代原来的 RTL build 页,而是补一层“通信工程师更熟悉”的解释。当前旧页面保持不动;这一页独立使用 lane01_comm_assets 下的样式、地图和 trace。
READING · 记忆视角
原文博客:You could have invented the Transformer from memoryWEB ↗golden_reference.py · forward_one_tokenALGO ↗transformer_layer.v · lane1 静态单层RTL ↗transformer_layer_runtime.v · lane0 TDMRTL ↗forward_core_5layer.v · 五层循环RTL ↗softmax_core.v · exp LUT + reciprocal LUTRTL ↗attention_score.v · q · K 相关器RTL ↗kv_cache_multi_layer.v · 多层延迟线RTL ↗
RUN & VERIFY
python golden_reference.py --max-new-tokens 20 --stream python sim/level3_transformer_layer/run_transformer_layer.py python sim/level4_forward_core/run_transformer_layer_runtime.py python sim/level4_forward_core/run_forward_core_5layer.py
trace 生成命令见上一节;完整 trace 文本在 lane01_comm_assets/traces/。地图和旧版页面都保留原文件,互不改动。
通信工程师的一句话版本:Transformer 用 key/value 记忆存储事实,用 q·k 相关器取回,用非线性与 softmax 压制多址干扰;lane1 是单码本接收机,lane0 是把同一套接收机 TDM 到五套码本。
FINAL TAKEAWAY
先有记忆和干扰,才有 attention 的结构;先有单层接收机,才有五层 TDM 复用。
如果一篇讲解让你先记住模块名,再看 RTL,那它还是在“从结果反推问题”。新版希望你先看到:为什么需要记住;为什么多个记忆会互相干扰;为什么非线性与 softmax 能提高容量;为什么五层同构后,layer_idx 和多层 cache 才会出现。RTL 是这些问题在面积、时序和存储约束下的一个具体答案。
communication lens · lane1 transformer_layer / lane0 transformer_layer_runtime
从联想记忆到 TDM 接收机:另一种理解 Transformer 的方式。
page: lane0_lane1_transformer_layer_comm_view.html · map: lane01_comm_assets/top_full_link_cc_zoom.svg
trace: lane01_comm_assets/traces/ · old page untouched