ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Verilog HDL学习笔记(二)| 第2章 层次建模的概念

Verilog HDL学习笔记(二)| 第2章 层次建模的概念 2.1 设计方法学Design Methodologies一、为什么需要设计方法学在正式开始写Verilog代码之前必须先理解一个问题如何组织一个大型的数字电路设计想象一下如果要设计一个包含几十万个逻辑门的CPU不可能一个人从头到尾把所有细节都想清楚。这就需要一套系统化的设计方法来管理复杂度。数字电路设计中有两种基本的设计方法在实际工程中这两种方法混合使用。二、自顶向下设计方法Top-Down核心思想从系统最顶层的功能需求出发逐层分解为更小的子模块直到最底层的实现细节。流程图┌─────────────────────────────────────────────────────────────────┐ │ 第1步定义顶层功能模块 │ │ 例如四位计数器 → 实现计数功能 │ └───────────────────────────┬─────────────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 第2步分析需要哪些子模块 │ │ 四位计数器需要4个T触发器 │ └───────────────────────────┬─────────────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 第3步继续分解子模块 │ │ T触发器 → D触发器 非门 │ └───────────────────────────┬─────────────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 第4步直到无法继续分解 │ │ D触发器 → 逻辑门非门 → 门级原语 │ └─────────────────────────────────────────────────────────────────┘优点设计思路清晰层次分明便于团队分工不同工程师负责不同层次的模块便于早期进行系统级仿真和架构评估顶层模块的接口定义明确后各子模块可以并行开发缺点对设计者的顶层规划能力要求较高如果顶层划分不合理后续修改代价较大三、自底向上设计方法Bottom-Up核心思想从最底层的基础元件开始设计然后用这些元件搭建更高层次的功能模块最终组合成完整的系统。流程图┌─────────────────────────────────────────────────────────────────┐ │ 第1步设计最底层元件 │ │ 例如设计D触发器、逻辑门 │ └───────────────────────────┬─────────────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 第2步用底层元件搭建中层模块 │ │ 用D触发器 非门 → 搭建T触发器 │ └───────────────────────────┬─────────────────────────────────────┘ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 第3步用中层模块搭建顶层模块 │ │ 用4个T触发器 → 搭建四位计数器 │ └─────────────────────────────────────────────────────────────────┘四、混合设计方法实际工程中的做法真实项目中这两种方法是结合使用的自顶向下架构师 ↓ ┌─────────────────────┐ │ 系统顶层划分 │ │ 定义模块接口 │ └──────────┬──────────┘ ↓ ┌────┴────┐ ↓ ↓ ┌─────────┐ ┌─────────┐ │ 模块A │ │ 模块B │ ← 自顶向下继续分解 │ 高层│ │ 高层│ └────┬────┘ └────┬────┘ ↓ ↓ ┌─────────┐ ┌─────────┐ │ 底层库 │←─────────┤ ← 自底向上提供基础元件 │已验证│ │ └─────────┘ └─────────┘ ↑ 自底向上电路设计师架构师从顶层向下分解功能模块电路设计师从底层向上优化基本元件两者在中间层次会合。2.2 四位脉动进位计数器4-bit Ripple Carry Counter一、什么是脉动进位计数器脉动进位计数器Ripple Carry Counter是一种最简单的计数器结构由多个T触发器级联而成。工作原理每个T触发器在时钟下降沿或上升沿翻转一次前一级T触发器的输出作为后一级T触发器的时钟信号因为时钟信号像水波一样逐级脉动传递所以称为脉动进位计数器结构框图二、T触发器T Flip-Flop功能每来一个时钟脉冲输出状态翻转一次0→1→0→1→...T触发器可以用D触发器 非门实现工作过程q输出0 → 非门输出1d 1时钟下降沿到来D触发器锁存d的值 → q变为1q变为1 → 非门输出0d 0下一个时钟下降沿到来q变为0如此循环每个时钟周期翻转一次三、D触发器D Flip-Flop with Asynchronous ResetD触发器的功能很简单在时钟有效边沿到来时将输入d的值传输到输出q。带异步复位的D触发器增加了复位功能当reset 1时立即将q清零不等待时钟当reset 0时正常工作2.3 模块Module一、什么是模块模块Module是Verilog中最基本的设计单元。每一个模块都对应一个具有特定功能的硬件电路。可以把模块理解为C语言中的函数有输入参数input、返回值output、内部局部变量reg/wire电路中的芯片有输入引脚、输出引脚、内部逻辑电路二、模块的语法结构module 模块名 ( 端口列表 ); // 1. 端口声明input / output / inout // 2. 数据类型声明wire / reg / parameter // 3. 功能描述assign / always / 模块实例调用 endmodule各部分说明三、模块的四种抽象层次Verilog允许在模块的内部实现中使用不同的抽象层次重点理解同一个模块对外接口端口保持不变内部可以更换不同的实现层次。这是Verilog设计的核心优势之一。// 同一个四选一多路选择器可以用三种方式实现 // 方式1门级实现用逻辑门 // 方式2数据流实现用assign和操作符 // 方式3行为级实现用always和case // 三种实现方式的端口完全相同可以互相替换2.4 模块实例Module Instantiation一、模块与实例的区别这是初学者最容易混淆的概念之一必须彻底理解关键理解一个模块可以被实例化多次每个实例都是独立的拥有自己的存储单元和信号连接每个实例必须有唯一的名字二、实例化的语法模块名 实例名 ( 端口连接 );示例// 定义一个T触发器模块模板 module T_FF(q, clk, reset); // ... endmodule // 在顶层模块中实例化4个T触发器 module ripple_carry_counter(q, clk, reset); output [3:0] q; input clk, reset; // 方式1按顺序连接端口 T_FF tff0(q[0], clk, reset); T_FF tff1(q[1], q[0], reset); T_FF tff2(q[2], q[1], reset); T_FF tff3(q[3], q[2], reset); // 方式2按名字连接端口推荐可读性更好 // T_FF tff0 (.q(q[0]), .clk(clk), .reset(reset)); endmodule端口的两种连接方式对比三、模块实例化的规则实例名必须唯一同一个模块的多个实例必须有不同名字模块不能嵌套定义不能在module ... endmodule内部再定义另一个模块实例化时可以省略实例名仅限门级原语and (out, a, b);合法2.5 逻辑仿真的构成一、仿真两个组成部分一个完整的Verilog仿真由两个独立的部分组成┌─────────────────────────────────────────────────────────────────────┐ │ 完整的Verilog仿真 │ │ │ │ ┌─────────────────────────┐ ┌──────────────────────────────┐ │ │ │ 激励块Testbench │ │ 设计块Design │ │ │ │ │ │ │ │ │ │ • 产生输入信号 │───→│ • 实现核心功能 │ │ │ │ • 监视输出信号 │←───│ • 是真正被测试的电路 │ │ │ │ • 检查功能正确性 │ │ │ │ │ └─────────────────────────┘ └──────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘设计块Design Block实现具体功能的Verilog模块是被测试的对象DUTDesign Under Test通常有输入端口和输出端口激励块Testbench / Stimulus Block为设计块提供输入信号监视和检查设计块的输出通常没有端口顶层模块二、两种激励模式模式一激励块直接调用设计块┌─────────────────────────────────────────────────────────────────┐ │ 激励块顶层模块 │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ ① 产生激励信号clk、reset、data等 │ │ │ │ ↓ │ │ │ │ ② 实例化设计块将激励信号连接到设计块输入端 │ │ │ │ ↓ │ │ │ │ ③ 监视设计块的输出 │ │ │ │ ↓ │ │ │ │ ④ 检查输出是否符合预期 │ │ │ └─────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘模式二顶层模块分别调用激励块和设计块┌─────────────────────────────────────────────────────────────────┐ │ 顶层模块无端口 │ │ │ │ ┌───────────────┐ ┌─────────────────────────────────────┐ │ │ │ 激励块 │ │ 设计块 │ │ │ │ (Stimulus) │───→│ (Design) │ │ │ │ │←───│ │ │ │ │ 产生/监视信号 │ │ 实现核心功能 │ │ │ └───────────────┘ └─────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘两种模式的对比2.6 四位脉动进位计数器——完整代码与逐行讲解一、设计块的完整实现1顶层模块ripple_carry_counter// // 模块名ripple_carry_counter // 功能四位脉动进位计数器 // 输入clk时钟信号reset复位信号高电平有效 // 输出q[3:0]4位计数值 // module ripple_carry_counter(q, clk, reset); // ---- 端口声明 ---- output [3:0] q; // [3:0]表示4位向量q[3]是最高位q[0]是最低位 input clk, reset; // clk和reset是1位标量输入 // ---- 实例化4个T触发器 ---- // 脉动进位结构前一级的输出作为后一级的时钟输入 T_FF tff0(q[0], clk, reset); // 实例0直接使用系统时钟clk T_FF tff1(q[1], q[0], reset); // 实例1时钟来自q[0] T_FF tff2(q[2], q[1], reset); // 实例2时钟来自q[1] T_FF tff3(q[3], q[2], reset); // 实例3时钟来自q[2] endmodule逐行深度讲解2中层模块T_FF// // 模块名T_FF // 功能T触发器Toggle Flip-Flop // 原理每个时钟周期翻转一次输出取反 // 实现方式D触发器 非门反馈连接 // module T_FF(q, clk, reset); // ---- 端口声明 ---- output q; input clk, reset; // ---- 内部连线声明 ---- wire d; // d是内部节点连接D触发器的输入端 // ---- 实例化D触发器 ---- D_FF dff0(q, d, clk, reset); // 注意端口顺序输出, 输入, 输入, 输入 // ---- 实例化非门 ---- not n1(d, q); // d ~q将输出q取反后反馈到输入端d endmodule逐行深度讲解T触发器工作原理初始状态假设q 0非门输出d ~0 1时钟下降沿到来时D触发器将d1锁存到q现在q 1非门输出d ~1 0下一个时钟下降沿q变为0如此往复每个时钟周期q翻转一次3底层模块D_FF// // 模块名D_FF // 功能带异步复位的D触发器 // 触发方式时钟下降沿negedge触发 // 复位方式异步复位reset上升沿立刻生效 // module D_FF(q, d, clk, reset); // ---- 端口声明 ---- output q; input d, clk, reset; // ---- 数据类型声明 ---- reg q; // q需要保持状态必须声明为reg类型 // ---- 行为描述 ---- // 敏感列表reset的上升沿 或 clk的下降沿 // 当这两个事件中的任何一个发生时执行always块内的语句 always (posedge reset or negedge clk) begin if (reset) // 如果reset 1 q 1b0; // 异步清零立即生效不等待时钟 else // 如果reset 0 q d; // 在clk下降沿将d的值锁存到q end endmodule逐行深度讲解关于阻塞赋值 vs 非阻塞赋值初学者记住在always (posedge clk)这样的时序逻辑块中一律使用非阻塞赋值。这是Verilog最重要的规则之一。二、激励块Testbench的完整实现// // 模块名stimulus // 功能测试四位脉动进位计数器 // 注意此模块没有端口是仿真顶层模块 // module stimulus; // ---- 声明激励信号 ---- reg clk; // 时钟信号由测试模块产生所以是reg reg reset; // 复位信号由测试模块产生所以是reg wire [3:0] q; // 计数输出来自设计模块只能观测所以是wire // ---- 实例化被测试的设计模块 ---- ripple_carry_counter r1(q, clk, reset); // 端口连接顺序q, clk, reset与模块定义顺序一致 // ---- 产生时钟信号 ---- initial begin clk 1b0; // 时钟初始化为0 forever #5 clk ~clk; // 每5个时间单位翻转一次 end // 这样产生的时钟周期为10个时间单位占空比50% // ---- 产生复位信号 ---- initial begin reset 1b1; // 初始时刻复位有效 #15 reset 1b0; // 15个时间单位后释放复位 #180 reset 1b1; // 180个时间单位后再次复位 #10 reset 1b0; // 10个时间单位后释放复位 #20 $finish; // 20个时间单位后结束仿真 end // ---- 监视输出信号 ---- initial $monitor($time, Output q %d, q); // $time当前仿真时间 // %d以十进制格式显示q的值 // 每当q发生变化时自动打印一行 endmodule逐行深度讲解initial块执行时间线仿真时刻 0 clk 0, reset 1 仿真时刻 5 clk 1第一次翻转 仿真时刻 10 clk 0第二次翻转 ... 仿真时刻 15 reset 0释放复位 ... 仿真时刻 195 reset 1再次复位 ... 仿真时刻 205 reset 0释放复位 ... 仿真时刻 225 $finish结束仿真三、完整的仿真输出结果分析0 Output q 0 ← 初始状态 20 Output q 1 ← 第1个计数 30 Output q 2 ← 第2个计数 40 Output q 3 50 Output q 4 60 Output q 5 70 Output q 6 80 Output q 7 90 Output q 8 100 Output q 9 110 Output q 10 120 Output q 11 130 Output q 12 140 Output q 13 150 Output q 14 160 Output q 15 170 Output q 0 ← 计满16后归零4位计数器模16 180 Output q 1 190 Output q 2 195 Output q 0 ← 复位信号触发计数器清零 210 Output q 1 220 Output q 2数据分析具体代码已在Quartus复现完成
返回列表