第 3 章 · 当庭陈述:赋值语义 章节摘要:本章跟着「一份陈述如何被采信」的主线走:assign 连续赋值陈述组合关系,always 过程块陈述响应规则,阻塞与非阻塞赋值的分界线决定陈述是否被正确采信,分支循环与任务函数则规定了陈述的修辞边界。这是全书技术密度最高的一章。 一条主线 主线是一场质证:你在代码里写下的每一次赋值,都是一份陈述,而听陈述的有两方——仿真器按调度规则执行陈述,综合器按映射规则把陈述变成门电路。两方的「听力」并不相同:有些陈述两方理解一致,这些是安全区;有些陈述只有一方听得懂,或者两方理解相左,这些就是事故段。
章节摘要:本章跟着「一份陈述如何被采信」的主线走:assign 连续赋值陈述组合关系,always 过程块陈述响应规则,阻塞与非阻塞赋值的分界线决定陈述是否被正确采信,分支循环与任务函数则规定了陈述的修辞边界。这是全书技术密度最高的一章。
主线是一场质证:你在代码里写下的每一次赋值,都是一份陈述,而听陈述的有两方——仿真器按调度规则执行陈述,综合器按映射规则把陈述变成门电路。两方的「听力」并不相同:有些陈述两方理解一致,这些是安全区;有些陈述只有一方听得懂,或者两方理解相左,这些就是事故段。本章按危险程度递进:先审最安全的 assign,再审弹性最大的 always,然后直面对全书最重要的分界线——阻塞与非阻塞,最后交代分支、循环与任务函数这些修辞工具的边界。
第一站 3.1「assign 连续赋值」:数据流建模的主力设施。目标必须是 net、随时待命重新求值、可以挂在延迟——这一站还要解释「连续」的确切含义:不是反复执行,而是「永远有效」的映射关系。
第二站 3.2「always 与 initial」:过程块的两种身份。always 是持续响应的常设岗位,initial 是仿真开场的一次性动作;阻塞与非阻塞的区别在本站从时序对照图讲起,这是 RTL 设计的第一生存技能。
第三站 3.3「分支与循环」:if 与 case 在硬件里长成优先级链与并行选择器,for 循环会被展开成实际电路,while 这类不定长循环在可综合世界寸步难行。软件直觉在这一站要接受最彻底的改造。
第四站 3.4「function 与 task」:陈述的复用机制。function 是纯组合函数,task 可以携带时序;两者的综合边界、以及在测试台里的用法,决定了它们各自的活动范围。
本章最大的拐点是接受「赋值不是动作而是关系」。软件思维里 a = b 是一个时刻发生的动作;硬件思维里 assign a = b; 是一根永远存在的连线,m <= n; 是「下个时钟沿 m 变成 n 的旧值」的契约。把动作思维换成关系思维,阻塞非阻塞的选择就不再是背诵的规则,而是必然的推论。
结论落在两条铁律上:时序逻辑用非阻塞赋值,组合逻辑用阻塞赋值。这两条不是风格偏好,而是仿真调度语义决定的正确性要求——违反它们的代码在简单场景下往往「碰巧能跑」,在复杂场景下产生依赖工具实现的随机行为,是全行业血泪最多的坑。本章的案例会演示违反铁律的典型翻车现场。
铁律之外还有一条方法论提示:本章的所有语义规则,都不需要死记。赋值语义、调度次序、分支推断,背后各有一套可以推导的机制——非阻塞的记账模型、事件队列的分区结构、保持旧值的电路含义。把机制搞懂,规则就是推论;把机制跳过,规则就是咒语。本书选择前者,希望你读完这章后,遇到任何赋值相关的争议都能自己推一遍得出答案。
陈述规则清楚了,但陈述何时发生、间隔多久、由什么触发,是第四章「时间证词」的领地。敏感列表怎么写才完整、延迟控制有哪些模型、时钟与复位怎么建模——时间维度的语法,是让正确陈述在对的时刻被听见的前提。