Altifigence Academy

6 / 20 · 实验

实验:有符号 MAC 处理单元

使用独立模型检查有效操作数、停顿和清零。

运行单个 PE 的约定模型

将代码保存为 pe-lab.mjs,用 Node.js 运行 node pe-lab.mjs。它不使用外部软件包,并输出 PE lab passed。这验证的是算术与控制模型,不会编译 SystemVerilog,也不证明在 DDS 中执行过。可将普通的两项任务理解为 W=8、K=2、AW=17,而模型使用宽整数作为参考。

PE 分别接收 A 和 B 的有效标志。只有两者都为真时才累加。转发遵循各操作数自身的有效标志。后续课程加入标签,以确保两个有效操作数属于同一归约项。

javascript
import assert from 'node:assert/strict';
const pe = {acc:0,a:0,b:0,av:false,bv:false};
function edge({a=0,b=0,av=false,bv=false,
               advance=true,clear=false,reset=false}={}) {
  if (reset || clear) {
    Object.assign(pe,{acc:0,a:0,b:0,av:false,bv:false});
  } else if (advance) {
    if (av && bv) pe.acc += a*b;
    Object.assign(pe,{a,b,av,bv});
  }
  return {...pe};
}
edge({clear:true});
assert.equal(edge({a:-2,b:3,av:true,bv:true}).acc,-6);
const held={...pe};
assert.deepEqual(edge({a:99,b:99,av:true,bv:true,advance:false}),held);
assert.equal(edge({a:4,b:-5,av:true,bv:true}).acc,-26);
assert.equal(edge({a:8,b:8,av:true,bv:false}).acc,-26);
assert.equal(pe.av,true); assert.equal(pe.bv,false);
edge({clear:true,a:100,b:100,av:true,bv:true});
assert.equal(pe.acc,0); assert.equal(pe.av,false);
assert.equal(edge({a:-128,b:-128,av:true,bv:true}).acc,16384);
edge({reset:true,advance:false});
assert.deepEqual(pe,{acc:0,a:0,b:0,av:false,bv:false});
console.log('PE lab passed');

预测并质疑结果

第一对输入产生 −6。暂停时忽略输入端提供的 99,并保持全部状态。下一对有效输入贡献 −20,使结果达到 −26。未配对的 A 令牌会转发,但不参与 MAC。clear 优先于同时出现的有效输入对;之后最小值×最小值得到 16384。即使 advance 为假,reset 也会清除有效状态。

在 always_ff 中实现相同优先级,并明确声明有符号 product 和 addend 信号。建立阵列模型时,应读取旧状态,写入独立的下一状态。原地更新 PE 对象,可能意外地让令牌在一个时钟沿内跨越多个 PE。

把累加条件从 AND 改为 OR,未配对令牌的断言必须失败。也可以尝试只让 advance 控制累加器,而允许转发继续移动。解释每项断言保护了哪条约定;仅仅显示通过消息,并不是完整的学习成果。

分别记录提供的操作数与已使用操作数:暂停期间的九十九在接口上可见,却从未被使用。封装逻辑必须保留下一个计划项,直到 advance 恢复。返回的状态副本保留先前观察值。将这些检查转化为基于波形的测试平台时,除了累加器,也应检查转发有效标志。

实际 RTL:有符号 MAC PE

将附件 signed_mac_pe.sv、systolic_2x2.sv 和 tb_systolic_2x2.sv 保存到同一位置。前两个文件是可综合 RTL;测试平台仅用于仿真。PE 在累加前,将完整的 2W 位乘积符号扩展到 AW。W=8、K=4 的检查使用 AW=18,验证 65536、−65024 和 64516。有序 k 标签通过 protocol_error 拒绝不匹配项。

优先级依次为 reset、clear、advance。Clear 在第一个数据时钟沿之前完成。每个上升沿后观察更新的累加器;相邻单元在下一个推进时钟沿使用已寄存的转发值。暂停保持每个数值、有效标志、标签和计数。两次 clear 之间运行一个任务,不包含输出量化或 DDS 集成。

安装 Icarus Verilog 后,运行:

bash
iverilog -g2012 -s tb_systolic_2x2 -o systolic.vvp signed_mac_pe.sv systolic_2x2.sv tb_systolic_2x2.sv
vvp systolic.vvp

预期输出 RTL PASS:;失败通过 $fatal 终止。修改 addend,使其使用零扩展,复现负矩阵失败。解释第一个不匹配的 PE 累加器,恢复符号扩展并重新运行。分别保留 Node 和 HDL 日志。

自己试试

在 (−2,3) 和 (4,−5) 之间插入有效输入对 (0,7)。和是多少?有效零与无效令牌有什么区别?

阅读解释

和为 −26。有效零仍占用一个真实的 k 项,并推进项计数和标签。无效令牌不占用任何项。对于三项任务,应相应更新 K、位宽分析和完成控制。

Lesson files

pe-lab.mjs ↓signed_mac_pe.sv ↓systolic_2x2.sv ↓tb_systolic_2x2.sv ↓

你的选择适用于此浏览器,可随时在页脚更改。