阶段一:同步 RTL 与 SPI Master(01–06)
工程基线:RTL、testbench、接口讲义。所有时间都以接受 start 的系统时钟上升沿为 t=0;表格描述理想 RTL 更新,不包括引脚传播延迟。
第 01 课内的基础入门顺序
SPI 是第一个完整项目。尚未系统学过数字 IC 时,先按下面的顺序拆解它的组成电路,再进入完整接口和波形。以下是第 01 课内的基础练习清单,不另设课号,不改变现有 SPI 规格;单独的微实验 RTL 尚未提供。
| 顺序 | 内容与练习 | 必须能解释什么 |
|---|---|---|
| 1 | 二进制、布尔逻辑、补码、位宽:解释 8'h80、8'hFF 和加法截断 | 相同位模式如何表示不同数值;溢出如何发生 |
| 2 | 组合 MUX、加法器、比较器与译码器:画输入到输出的电路 | 组合逻辑不保存状态;完整赋值为何避免 latch |
| 3 | DFF 与 8 位寄存器:加入 enable、同步低有效复位 | Q→MUX→D 的保持与更新,以及复位采样窗口 |
| 4 | 两个寄存器同拍更新:推演 a<=b、b<=a | 非阻塞赋值读取旧状态,与软件逐句执行的区别 |
| 5 | 计数器与终值使能:推演 0…24 的事件间隔 | 位宽、off-by-one 和 clock enable;所有寄存器仍由 clk 驱动 |
| 6 | 移位寄存器:逐拍发送 A5,接收独立的 3C | 串并转换、位序、旧值与最后一位的拼接 |
| 7 | FSM:画 IDLE/TRANSFER/FINISH 和 busy/done | 请求接受、忙时拒绝、有限结束和单周期完成 |
| 8 | testbench:先预测,再给独立输入并检查输出 | 如何发现错误;覆盖正常、边界和复位中止 |
| 9 | 连接完整 SPI Mode 0 Master,接下面的 01–06 | 首位提前准备、8 次采样、CS 尾部保持和结果有效时刻 |
| 10 | 接 07–10 的综合与 STA | RTL 如何映射成单元,setup/hold 与负载/PVT 的关系 |
基础练习与首次理解 SPI 建议预留 20–30 小时,计入基础课程原有时间预算;第 10 项按 07–10 的安排展开。组合过程通常用阻塞赋值,时序过程用非阻塞赋值,但学习重点是它们描述的电路和更新时刻。
从模拟背景出发,可把 DFF 理解为有 setup/hold 要求的状态采样元件,组合路径理解为有负载与 PVT 依赖的逻辑网络。数字抽象中的 0/1 和理想边沿便于推演,实际门延迟、斜率和亚稳态仍需对应时序与电路分析。
第一份学习产物:画带 enable 和同步复位的 8 位寄存器,列出 reset/enable/data 的优先级,手算三个 clk 边沿后的 Q;然后推演双寄存器交换。按“画电路→预测→写 RTL→独立仿真检查”推进,再阅读完整 SPI。
01:先把规格写成契约
目标和讲解
SPI 有移位功能,也有接口协议。Mode 0 表示 CPOL=0、CPHA=0:SCLK 空闲低,上升沿采样,下降沿准备下一位。全双工意味着 MOSI/MISO 在同一组时钟内同时传输。
模块接口必须决定何时接受数据。当前 Master 只有 IDLE 且 start=1 的系统时钟边沿才接受请求,并锁存 tx_data。忙时请求直接忽略,没有隐藏队列。start 要是一个 clk 周期的脉冲。
对于模拟工程师,接口契约相当于电路的工作条件:只说明“典型情况下能动”远远不够,还要给出输入有效窗口、异常行为和输出的使用时机。
| 事件 | 接受时刻/结果 | 需要调用方保证 |
|---|---|---|
| 发起传输 | IDLE 中 clk 上升沿 | start 单周期,tx_data 满足该边沿时序 |
| 传输期间改变 tx_data | 原交易继续 | 不把实时输入当成已接受数据 |
| busy 中再次 start | 丢弃 | 需要排队时由上层实现 |
| 第八次采样 | rx_data 更新 | 此时还没有 done |
| CS 释放 | done=1、busy=0 | 在 clk 域消费完成信号 |
| 同步复位采样为低 | 中止、回空闲、结果清零 | rst_n 低覆盖至少一个 clk 上升沿 |
消费方若同样用 always_ff @(posedge clk),会在 done 置高后的下一个上升沿读到旧 done=1,同时读取已稳定的 rx_data;这是正常的寄存器间同步传输。
练习与验收
画出 start、busy、cs_n、done 的关系。补充一个反例:start 一直高到下一次 IDLE,会发生什么?验收要求能解释“发出请求”和“请求被接受”的区别,不把 busy-start 丢弃说成排队。
02:先预测 A5/3C 波形
目标和讲解
系统周期 20 ns,分频 D=25,所以半周期 H=D×20 ns=500 ns。接受请求后立即准备 MOSI[7];第一采样沿在 H 之后出现。第 k 次采样位于 (2k−1)H,第 k 次下降沿位于 2kH,k=1…8;CS 释放位于 17H。
| k | 时间/μs | MOSI=A5 的采样值 | MISO=3C 的采样值 | bit_count |
|---|---|---|---|---|
| 1 | 0.5 | 1 | 0 | 0 |
| 2 | 1.5 | 0 | 0 | 1 |
| 3 | 2.5 | 1 | 1 | 2 |
| 4 | 3.5 | 0 | 1 | 3 |
| 5 | 4.5 | 0 | 1 | 4 |
| 6 | 5.5 | 1 | 1 | 5 |
| 7 | 6.5 | 0 | 0 | 6 |
| 8 | 7.5 | 1 | 0 | 7 |
第八次采样形成 3C;8.0 μs 出现最后下降沿;8.5 μs 释放 CS,done 高 20 ns。有效传输的八个时钟周期是 8 μs,CS 有效时间是 8.5 μs。
对应模拟电路的思考:数字波形图通常省略传播延迟和斜率,但真实接收器需要稳定窗口。RTL 中一条竖线不代表物理引脚在零时间内变化。
练习与验收
在 现有 SVG 上标出 t=0、八个上升沿、最后下降沿、done。产物目录若未收集,先按 运行指南 收集已有结果。用上表逐项对照,能说明为什么首位不需要先移位。
03:把 RTL 翻译成寄存器和门
目标和讲解
always_ff @(posedge clk) 描述寄存器。每个边沿,右侧表达式读取更新前的状态,非阻塞赋值安排新的 Q 值。它是并行的状态更新,不是软件逐句立即改变量。
tx_shift <= {tx_shift[6:0], 1'b0};
mosi <= tx_shift[6];
假设旧 tx_shift=A5,更新后 tx_shift=4A,mosi=旧 bit6=0。若错误地理解为先移位再取 bit6,会得到 1,丢失正确的下一位。
使能式寄存器的方程是 Q_next = enable ? new_data : Q。没有专用使能触发器时,可由 D 端 MUX 和普通 DFF 实现。“此周期保持”也有电路含义:反馈选择,而不是软件暂停。
{rx_shift[6:0], miso} 主要是连线重排;计数器的 +1 则会形成组合逻辑。FSM 的 case 会形成状态译码与选择网络。组合过程使用 always_comb 时,需要给所有输出完整赋值,否则可能推断 latch;时序过程有意保持 Q 不会因此推断 latch。
对模拟工程师而言,最有用的图是 Q→组合逻辑→D:Q 是当前状态,D 是下一状态,时钟决定采样时刻。
还要注意位宽与四态语义。8 位无符号计数从 FF 加 1,写回 8 位结果会截断成 00;中间表达式位宽、无尺寸常量和 signed/unsigned 混用可能产生不同的扩展与比较行为,所以写清位宽和符号。8'h80 按无符号数是 128,按有符号二进制补码是 −128。
仿真中的 X 表示未知,Z 表示高阻。普通 == 比较含未知位时可能返回 X;testbench 用 !== 将未知也判为不符合预期。它不能代替对硬件复位、初始化和真实三态结构的设计。组合逻辑一般用阻塞赋值表达临时计算,时序逻辑用非阻塞赋值描述状态更新。
练习与验收
画出 tx_shift、rx_shift、rx_data 和 mosi 的 D/Q 连线。推导第八次采样时,为什么 rx_data 要取 {旧 rx_shift[6:0], miso},不能直接取旧 rx_shift。验收时不依赖逐句软件解释。
04:计数器、使能和状态机
目标和讲解
计数器从 0 数到 D−1,在命中终值的边沿执行一次“半周期事件”,随后清零。这些事件控制 SCLK 输出翻转,内部没有把 SCLK 当作寄存器时钟。
分频位宽是 max(1, ceil(log2(D)))。D=25 需要 5 位;D=1 仍保留合法的 1 位声明,每周期触发一次半周期事件。
stateDiagram-v2
[*] --> IDLE
IDLE --> TRANSFER: start 被接受
TRANSFER --> TRANSFER: 继续采样或移位
TRANSFER --> FINISH: 第八个下降沿
FINISH --> IDLE: CS 保持结束,doneFINISH 提供完整的末尾 CS 保持时间;它不是多余等待。bit_count 只在前七个下降沿增加,采样时依次为 0…7。
整数分频使 f_sclk=50 MHz/(2D)。要 2 MHz,需要 D=12.5,当前架构无法精确产生。D=12 为 2.0833 MHz,D=13 为 1.9231 MHz。交替半周期可以逼近平均频率,但会引入不同的边沿间隔,需要另写规格,不在本课加入。
练习与验收
计算 D=1、3、10、25 的频率、半周期和 CS 有效时间。D=10 可通过现有 runner 单独运行:
ssh -T -o BatchMode=yes IC_Server "bash /home/userone/AAAIC/test_tb/digital_ic_learning/lessons/01_spi_master/scripts/run_sim.sh 10"
不需要修改默认 RTL 参数。D=10 结果位于独立 div10 目录,默认收集脚本仅收集 1、3、25;需要 div10 时单独 scp 下载并核对 SHA-256。验收要求预测频率与 3.4 μs CS 时间,再对照波形。
05:复位与边界行为
目标和讲解
同步低有效复位只有在 clk 上升沿才能更新寄存器。信号名 rst_n 的后缀表示极性,不表示异步;敏感列表和单元实现决定复位方式。
当前设计传输中复位会撤销 CS、清零 rx_data,不发 done。复位优先级高于 start。rst_n 的脉冲若完全落在两次 clk 上升沿之间,当前 RTL 不保证响应。
复位也是时序输入:同步复位必须满足 setup/hold;异步复位的释放需要考虑 recovery/removal。不要因为“复位很慢”就把所有复位路径 false path。
完成还有两个时刻:rx_data 在第八次采样更新,done 在 CS 释放时出现。二者之间的延迟用于完成协议尾部。上层用 done 作为有效标志,可以避免在中途读取旧交易结果。
练习与验收
列出复位和 start 同时出现、busy-start、第八次采样后复位、FINISH 中复位、复位后第一笔交易的行为。解释 done 为什么先默认清零、只在完成分支置高。能区分协议中止与正常完成。
06:用验证发现自己看不见的错误
目标和讲解
testbench 的独立从设备给出 MISO,同时检查 MOSI,不使用唯一的回环结果来证明收发正确。A5/3C 刻意让收发字节不同。256 个发送模式能覆盖每一位取值与排列,但仍不是对全部状态序列的穷尽证明。
| 检查 | 主要发现什么 |
|---|---|
| 256 个发送值,独立接收 | 位序、遗漏、收发耦合错误 |
| 接受后改变 tx_data | 未锁存输入 |
| busy 中再次 start | 非预期重启 |
| 8 升沿、8 降沿与间隔 | 计数 off-by-one、分频错误 |
| CS setup/hold | 首位准备和尾部结束错误 |
| reset abort + recovery | 中止时误发 done、残留状态 |
| done 单周期、总完成计数 | 重复完成或丢失完成 |
| 超时与非零退出 | 死锁及自动化漏判 |
现有 testbench 在 clk 上升沿后延迟 1 ns 检查输出,以观察非阻塞更新后的值。这个延迟是 testbench 的调度手段,不能移进可综合 RTL 当作真实延迟单元。
故障注入实验:保留当前改动后,一次只引入一个错误,例如把接受时的首位 MOSI 从 bit7 改为 bit6。运行测试,记录它在哪个检查点失败,再精确恢复这一行。不要以“仿真波形看起来差不多”代替自动检查。
练习与验收
写出至少两个注入错误与预计触发的检查。实验后恢复基线,执行 运行指南 中 sim→synth 流程。分频 1、3、25 必须各为 258 笔 PASS,总计 774 笔完整交易;脚本出现 SPI_SIMULATION_COMPLETE,综合出现完成标记并检查网表和约束。能解释为什么这些检查仍不等于形式等价或真实外部接口验证。
阶段验收
不看答案,从 A5/3C 手画完整时序;解释所有寄存器的更新时刻;准确计算分频;列出接口异常行为;给出至少一次预期失败与恢复通过的证据。答案见 01–06 参考。