概述
1 数字逻辑电路设计与 Verilog 代码开发
如果想用 Verilog 语言写出一个真正可以物理实现的电路,必须先进行电路设计再进行代码编写。
1.1 面向硬件电路的设计思维方式
核心:数据通路(Datapath)+控制逻辑(Control Logic)
-
数据通路:数字系统中,各个子系统通过数据总线连接形成的数据传送路径称为数据通路。
电路中各器件之间传递的是:电磁信号
电路结构图:用来刻画电路中的数据通路
## 1.2 行为描述的 Verilog 编程风格
通常来说,使用 Verilog描述电路有两种编程风格,一种叫行为描述,一种叫电路描述。
-
行为描述:侧重于对模块行为功能进行描述
优点:代码表达直观,编码效率高,易维护 缺点:EDA工具在综合阶段根据 Verilog 代码推导出的电路的行为未必和设计者的预期一致
-
电路描述:直接对电路的逻辑进行具体描述
## 1.3 自顶向下的设计划分过程
在考虑一个较为复杂的电路系统的设计方案时,建议采取“ 自顶向下、模块划分、逐层细化”的设计步骤
## 1.4 常用数字电路的 Verilog 描述
#### verilog语法
module/endmodule :表征模块的开始与结束。
gates :模块名可由用户指定,可包含字母、数字及下划线,需以字母开头,区分大小写
assign :赋值操作关键字,该关键字后可跟一个赋值表达式,该关键字是实现组合逻辑操作的一种主要描述方式。
input/output :表征该信号的方向,除输入、输出外还有一种inout(输入输出)型。
位宽:[3:0]表征该信号的位宽4位 0 1 2 3,实例中是推荐写法,[0:3]、[4:1]等写法也是合法的 wire和reg在声明时不做特殊声明表示只有一位
//、/* */ :代码注释,增加代码可读性,//为单行注释,/* */为多行注释
数字:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tgY27YWu-1598968260889)(C:UsersdellAppDataRoamingTyporatypora-user-imagesimage-20200901105650503.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AHh1zv3d-1598968260894)(C:UsersdellAppDataRoamingTyporatypora-user-imagesimage-20200901105710207.png)]
1.4.1 一些硬性规定
- CPU设计代码中 禁止出现 initial 语句。
- CPU设计代码中 禁止出现 casex、casez。
- CPU设计代码中 禁止出现用“#”表达电路延迟。
- CPU设计代码中时钟信号 clock 只允许出现在 always @(posedge clock)语句中。
- CPU设计代码中所有带复位的触发器, 要么全部是同步复位, 要么全部是异步复位。
1.4.2 模块声明和实例化
模块:一个包含输入和输出的的硬件块称为模块,例如:与门多路选择器优先级电路
描述模块功能的两种形式:
- 行为模型:描述一个模块干什么
- 结构模型:应用层次化方法描述一个模块怎么由更简单的部件构成
什么时候应该将一些逻辑封装为模块?
-
在设计中至少会被使用两次的逻辑,而且这个逻辑采用实例化模块的方式代码的易读性
(代码行数、代码含义)优于直接写逻辑,那么就封装成模块,如译码器、多路选择器。 -
功能规格十分明确,且与外界的交互信号数目不是很多的,那么就封装成模块,如 ALU、
regfile 之类的。个人感觉有点像vue的组件化,比如分页器/轮播图这种就可以拆分出一个模块,交互数目多不多 我感觉有点像父子组件传值 要是需要很多子传父 父传子就很麻烦 还不如不封装
-
现有的一个模块达到了数千行代码的规模,将其拆分成若干个小模块,譬如将一个 CPU
按照流水线划分成若干个模块。某一个模块过大需要拆分为多个小模块建议一个文件中只包含一个模块,便于后期代码维护
1.4.3 基础逻辑门
表述逻辑关系:&& ||
表述逻辑门:& |
### 1.4.4 运算符的优先级
1.4.5 译码器
1.4.6 编码器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QhfvtjUi-1598968260897)(C:UsersdellAppDataRoamingTyporatypora-user-imagesimage-20200901105036585.png)]
1.4.7 多路选择器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-07feOTZ0-1598968260910)(C:UsersdellAppDataRoamingTyporatypora-user-imagesimage-20200901120737712.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EVfVRk4M-1598968260913)(C:UsersdellAppDataRoamingTyporatypora-user-imagesimage-20200901120949441.png)]
1.4.8 简单 MIPS CPU 中的 ALU
assign lui_result = {alu_src2[15:0 , 16’b0]};
理解高位加载的含义 将小尾端 0—15位左移,后16位补零 提取指令中需要用到的数
ALU代码详细解说
1.4.9 触发器
### 1.4.10 MIPS CPU 中的寄存器堆
- 在一个单发射五级流水简单 MIPS CPU 中,
GR对应一个 32项、每项32位的寄存器堆 - 为了支持流水,该寄存器堆要能够每周期读出两个 32位
的数、写入一个 32 位的数 - MIPS CPU 还有一个特殊之处,即 0 号寄存器恒为 0
???5-32 译码器
1.4.11 RAM
这里说的 RAM 指的是SRAM,通常用来在 CPU 中实现指令存储器、数据存储器。
1.4.12 流水线
电路流水线
不被阻塞的流水线其实就是将多组触发器串联
后面的流水线出现阻塞,前面的流水线级需要将原有的数据保存在本流水级,否则仍继续向后传数据会丢失(可以不保存的条件:整个电子系统在上层协议栈中有数据丢失的检测重传机制)
要使流水线能够应对阻塞的情况,核心在于控制好各流水线缓存的写
使能信号
定义流水级有效位的好处是,清空流水线的时候不用把各级流水线 data 域的值都置为无效值,只需要将流水级的 valid 位清为 0 就可以了,可以节约逻辑资源。
2 数字逻辑电路功能仿真常见错误及其调试方法
2.1 功能仿真波形分析
2.1.1 观察仿真波形的思路
- 第一步 , 熟悉待调试的设计
- 第二步,找到一个你能明确的错误点
- 最后一步 , 从 沿着设计的逻辑链条逆向逐级查 看信号 ,直至找到源头。
含有时序逻辑的电路出错怎么分析?
- 先把需要观察的电路所用的时钟信号抓出来(避免错的时钟信号)
- 明确所观察的时序器件(如触发器、同步 RAM)是用时钟上升沿还
是下降沿触发的 - 之后就是一步步向前推
#### 2.1.2 提高波形分析效率的实用技巧
- 一次仿真记录所有信号的数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hc2AwOcc-1598968260916)(C:UsersdellAppDataRoamingTyporatypora-user-imagesimage-20200901161042678.png)]
-
给重要的时刻做标记
在波形上你关注的时刻处左击,此时游标(Cursor)将出现在你关注的
时刻,点击波形上方工具条中的 ,就做好了一个标记。之后无论你是直接移动波形至 Marker处还
是使用波形上方工具条中的 快速定位到标记处,都会大幅度提高定位效率。 -
熟练使用波形缩小、放大功能
-
将关联信号分割、分组
建立分割是在你在想加分割空行的位置的上一个信号处,对着信号名右键打开菜单栏选择“New Divider”即可。
删除分割就是点击分割,按 Del 键删除即可
建立分组是将准备放入一组的信号选上,然后右键打开菜
单栏选择“New Group”即可。 -
多位宽信号用值查找快速定位
方法就是对着待查找信号的信号名右键打开菜单栏选择“Find Value”,之后会在波形上方出现与搜索相关的工具栏,根据其提示输出数据就可以了。
Vivado 的 Xsim 的值查找不支持通配符,使用者只能通过调整查找工具栏中 Matching 的方式来部分实现模糊查找的功能。
### 2.2波形异常类错误的调试
波形异常类错误是指那些不需要分析电路设计的功能直接观察波形图就能判断的错
分类:1. 信号为“Z”。
- 信号为“X”。
- 波形停止。
- 越沿采样:上升沿采样到被采样数据在上升沿后的值。
- 其他,波形怪异:仿真波形图显示怪异,与设计的电路功能无关的错误。
2.2.1 信号为“Z”
“Z”表示高阻,比如电路断路了就是显示为高阻
原因:
-
RTL里声明的wire型的变量从未被赋值
-
模块调用的信号未连接导致的信号悬空
- 显式的未连接 (人为故意设置 只针对output类接口)
- 隐式的未连接 (疏忽 代码不规范)
output 类接口未连接是母模块里不使用该信号,可能是人
为故意设置的。所有的 input 类接口被调用时不允许悬空。
如何规避
- RTL编写时注意代码规范,特别是模块调用时,按接口顺序一一对应。
- 所有 input 类接口被调用时不允许悬空。
- 一旦发现一个信号为“Z”,向前追踪产生该信号的因子信号,看是哪个为“Z”,一直追踪下去直到追踪到该模块里的 input 接口,随后进行修正。
- 有可能“Z”只出现在向量信号里的某几位上,也是一样的追踪,有可能调用时某个接口存在宽度不匹配也会带来该接口上某些位为“Z”。
2.2.2 信号为“X”
“X”表示不定值
原因:
- RTL里声明为 reg型的变量,从未被赋值;
- RTL 里写成了多驱动的代码,有时候也可能导致“X”。(有些多驱动的代码,并不会导致“X”:有些多驱动代码可能会被 Vivado 自动处理,但其实是有风险的;有些多驱动代码可能会导致综合时失败,并且会明确报出多驱动的 Error)
如何规避
- 一旦发现仿真错误来自某个信号为“X”,则向前追踪产生该信号的因子信号,看是哪个为
“X”,一直追踪下去直到追踪到某个信号未赋值,随后修正。 - 如果因子信号都没有为“X”的,则很可能是多驱动导致的,则进行综合然后排查 Error 和
Critical warning。 - 寄存器型信号如果没有复位值,在复位阶段其值可能也为“X”,但可能这并不会带来错误。
- “X”和 1进行或运算结果为 1,“X”和 0进行与运算结果为 0。
2.2.3 波形停止
波形停止是指仿真停止在某一时刻,再也无法前进分毫,而仿真却显示不停地在运行,往往是由于 RTL里存在组合环路导致的。
所谓组合环路就是信号 A 的组合逻辑表达式中某个产生因子为 B,而 B 的组合逻辑表达式中又用到了信号 A.
处理步骤
- 一旦发现波形停止,则先对设计进行综合。
- 查看综合产生的 Error 和 Critical warning,并尝试修正。比如上图示例中的组合环路,经过
Vivado的综合后变成了一个多驱动的关键警告
2.2.4越沿采样
越沿采样是指一个被采样的信号在上升沿采样到了其在上升沿后的值,一般情况下认为这是一个错误,往往是由于 RTL里阻塞赋值“=”和非阻塞赋值“<=”使用不当导致的。
每一次赋值,分为两步:第一步,计算等式右侧的表达式;第二步,赋值给左侧的信号。这两步,简记为计算和赋值。
在一个上升沿到来时,所有由上升沿驱动的信号按以下顺序进行处理:
- 先处理阻塞赋值,先完成计算和赋值,同一信号完成计算后立马完成赋值。同一 always 块里的阻塞赋值从上到下按顺序串行执行,不同 always 块里的阻塞赋值依赖工具实现确定顺序串行执行,一一完成计算和赋值。=
- 再进行非阻塞赋值的计算。所有非阻塞赋值其等式左侧的值都同时计算好。<=
- 上升沿结束时,所有非阻塞赋值同时完成最终的赋值动作。
错因
越沿采样,除非特意设计,一般我们认为是一个设计错误
规避
- RTL编写时注意代码规范,所有 always 写的时序逻辑只允许采用非阻塞赋值。
- 一旦发现越沿采样的情况,追踪被采样信号,直到追踪到某一个阻塞赋值的信号,随后进
行修正。
2.2.5 波形怪异
将目前未能想到的波形出错的类型都归为波形怪异。
当出现波形怪异类的错时,需要区分其是仿真工具出错还是 RTL代码出错:
- 观察出错的信号,看其生成原因,如果确认 RTL 应该没有错,且波形显示确实太怪异(比如始终为 32’hxx?x0x?),则很有可能仿真工具出错。重启 Vivado 或重启电脑,甚至重建工
程试试。 - 实在无法从波形里区分出是什么错。可以尝试先运行综合,看出综合后的 Error、Criticalwarning 和 warning。其中 Error 是必须要修正的,Critical warning 是强烈建议要修正的,warning是建议尽量修正的。
- 经常有些不符合规范的代码,Vivado也不会报出 Warning,需要大家仔细复核自己的代码。
3 进一步使用 Vivado
3.1 定制同步 RAM IP 核
### 3.2 定制异步 RAM IP 核
### 3.3 查看时序结果和资源利用率
最后
以上就是平淡白猫为你收集整理的CPU设计——第三章 数字逻辑电路设计基本技能1 数字逻辑电路设计与 Verilog 代码开发的全部内容,希望文章能够帮你解决CPU设计——第三章 数字逻辑电路设计基本技能1 数字逻辑电路设计与 Verilog 代码开发所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复