深圳北站设计者,网站建设与管理专业介绍,网站现在一般做多大尺寸,北京电商网站建设公司目录0 前言0.1 使用环境0.2 知识点0.3 注意事项1 建模#xff1a;1位加法器1.1 构建基础模型1.1.1 一位加法器1.1.1.1 科技黑箱#xff1a;外部端口与功能1.1.1.2 揭秘黑箱#xff1a;内部结构与模块1.1.2 从顶层模块提取低层模块#xff1a;取反功能选择器1.1.2.1 科技黑箱…
目录0 前言0.1 使用环境0.2 知识点0.3 注意事项1 建模1位加法器1.1 构建基础模型1.1.1 一位加法器1.1.1.1 科技黑箱外部端口与功能1.1.1.2 揭秘黑箱内部结构与模块1.1.2 从顶层模块提取低层模块取反功能选择器1.1.2.1 科技黑箱外部端口与功能1.1.2.2 揭秘黑箱内部结构与模块1.1.3 从顶层模块提取低层模块基础加法器1.1.3.1 科技黑箱外部端口与功能1.1.3.2 揭秘黑箱内部结构与模块1.2 层次建模1.2.1 32位加法器1.2.2 1位加法器2 实现1位加法器2.1 取反选择器2.1.1 设计块2.1.2 激励块2.1.3 功能验证2.2 基础加法器2.2.1 设计块2.2.2 激励块2.2.3 功能验证2.3 1位加法器的实现2.3.1 设计块2.3.2 激励块功能验证3 原理讲解实现整数减法将负数转换为补码3.1 计算机进制数与人类进制数的差别3.1.1 十进制数转换为补码的原理3.2 进位输入信号carry_in的作用3 建模32位加法器3.1 科技黑箱外部端口3.2 揭秘黑箱内部细节3.2.1 组合式设计的核心原则4 实现32位加法器4.1 设计块4.2 激励块4.2.1 报错解决方案4.3 仿真验证5 行为级建模的魅力5.1 设计块5.2 激励块5.3 结果验证5.4 思考点6 由行为级描述掌握几大重要思想7 待求解决方案的错误0 前言
0.1 使用环境
EDA工具Vivado 2017.4硬件描述语言Verilog HDL
0.2 知识点
加法器全加器多路选择器Verilog 语言及其设计思想补码
0.3 注意事项
本文中所有的Verilog实现部分均完成了仿真测试并未进行时序验证以及后续的逻辑综合步骤后续步骤可能会出现问题请读者自行完成修改。
1 建模1位加法器
我们先来构建1位加法器要知道32位加法器只不过是将32个1位加法器以特定的方式连接起来真正的核心是1位加法器的设计。
1.1 构建基础模型
1.1.1 一位加法器
1.1.1.1 科技黑箱外部端口与功能
功能能够实现整数的加减法这也就意味着
支持正数和负数支持加法和减法事实上a(-b) a - b;负数与减法某种程度上来说是一样的能够识别正负数并且能够做出不同的选择 遇到正数、加法直接运算遇到负数、减法 转换为补码再运算 输入端口 数据ab进位carry_in取反控制a_invertb_invert 输出端口 和sum进位carry_out
特别注意对于多个加法器组合的情况carry_in是来自上一个加法器的carry_out如果位置是第一个则要置为0
1.1.1.2 揭秘黑箱内部结构与模块 内部线网不与外部相连的内部线就是内部线网在Verilog设计中需要单独加上
1.1.2 从顶层模块提取低层模块取反功能选择器
1.1.2.1 科技黑箱外部端口与功能
下图图示部分为取反功能选择器其功能是控制输入的数据是否执行取反操作 以下是其外部端口
1.1.2.2 揭秘黑箱内部结构与模块 多路选择器的全面讲解参考我的另一篇文章全面剖析数据选择器
1.1.3 从顶层模块提取低层模块基础加法器
1.1.3.1 科技黑箱外部端口与功能 1.1.3.2 揭秘黑箱内部结构与模块
使用数据流建模两个逻辑表达式就可以这个部分是数字逻辑必备知识不再赘述。
1.2 层次建模
1.2.1 32位加法器
32位加法器1位加法器12.....30311.2.2 1位加法器
1位加法器取反选择器1非门二选一选择器取反选择器2基础加法器与门或门2 实现1位加法器
2.1 取反选择器 2.1.1 设计块
timescale 1ns / 1ps// 取反选择器的设计
module date_invert_choose (input a, // 数据输入input a_invert, // 使能端 output a_out // 数据输出);choose_2to1 CH0 (a,~a,a_invert,a_out); // 调用二选一选择器实例endmodule// 二选一选择器
module choose_2to1 (input d0,d1, // 数据输入input add, // 地址控制output out // 数据输出);assign out (~add)d0 | addd1;endmoduleFPGA优化结果如下
2.1.2 激励块
设置激励块有两个口诀
调用模块建实例设置输入验输出
timescale 1ns / 1psmodule test;// 设置变量reg a; // 数据输入reg a_invert; // 控制输入wire a_out; // 数据输出 // 调用实例【调用模块建实例】date_invert_choose DIC0 (a,a_invert,a_out); // 【不要忘了实例名只有门级描述可以不写】// 设置信号【设置输入看输出】initialbegina 1;a_invert 0;#1 $display(功能%b直接输出a_out %b\n,a_invert,a_out);a_invert 1;#1 $display(功能%b取反输出a_out %b\n,a_invert,a_out);endendmodule2.1.3 功能验证
仿真结果没有问题
2.2 基础加法器
2.2.1 设计块
module add_base_1size (input a,b, // 数据输入input carry_in, // 进位输入output carry_out, // 进位输出output sum // 和);assign sum a ^ b ^ carry_in;assign carry_out ab | (a^b)carry_in;endmodule2.2.2 激励块
请读者自行完成
2.2.3 功能验证
经过仿真验证没有问题。
2.3 1位加法器的实现
本节展现的是完整代码包含2.1和2.2的设计块代码
2.3.1 设计块
timescale 1ns / 1ps// 取反选择器的设计
module date_invert_choose (input a, // 数据输入input a_invert, // 使能端 output a_out // 数据输出);choose_2to1 CH0 (a,~a,a_invert,a_out); // 调用二选一选择器实例endmodule// 二选一选择器
module choose_2to1 (input d0,d1, // 数据输入input add, // 地址控制output out // 数据输出);assign out (~add)d0 | addd1;endmodule// 基础加法器
module add_base_1size (input a,b, // 数据输入input carry_in, // 进位输入output carry_out, // 进位输出output sum // 和);assign sum a ^ b ^ carry_in;assign carry_out ab | (a^b)carry_in;endmodule// 1位加法器的实现
module add_1size (input a,b, // 数据输入input a_invert,b_invert, // 求反控制input carry_in, output carry_out,output sum);// 内部线网设置wire a_out,b_out; // 获得经过求反选择器之后的结果// 调用模块实例// 求反控制器实例date_invert_choose DateA (a,a_invert,a_out);date_invert_choose DateB (b,b_invert,b_out);// 加法器实例add_base_1size Add_A_B (a_out,b_out,carry_in,carry_out,sum);endmodule2.3.2 激励块功能验证
module test;reg a,b; // 注意reg为无符号数reg a_invert,b_invert;reg carry_in;wire carry_out;wire sum;add_1size AD0 (a,b,a_invert,b_invert,carry_in,carry_out,sum);// 设置信号求 a -b即 a - binitialbegina 0; b 0;a_invert 0; b_invert 1; // b取反carry_in 1; // b取反加1#1 $display(a %b, b %b, carry_out %b\na - b %b\n,a,b,carry_out,sum); // 模拟 a - bif (sum 0)$display(验证成功 0 - 0 0);endendmodule这里的验证我有必要讲解一下因为有些地方会令人费解。
reg为无符号数如果直接像reg a -1这样的语法存到计算机中是自动以补码形式存储的那样的话就没有办法验证我们的设计了。要明白的一点是我们是假定计算机不会自动将负数转换为补码由我们的设计来实现这个工作因此这里我直接手动输入了某个数的原码再有我们的设计将其转换为补码这样就完成了验证工作。我们假定在十进制下a 0-b 0我们要求出 a -b的值并且这台计算机没有将-b存储为补码的功能我们又知道b的二进制形式就是其补码它在经过全部按位取反再加1后会得到-b的补码因此也就有了激励块中的b 0;和carry_in 1;这是为了验证取反加1功能是否正确。实际的过程是这样的a b取反 1 a (-b) a - b这样就是能够让你明白两件事负数的补码转换和减法转换为加法它的实际过程是这样的a 0,b 0故a b取反 1 0 1 1 10也就是sum 0carry_out 1
3 原理讲解实现整数减法将负数转换为补码
本小节可以跳过可以看完第4节再回看。
3.1 计算机进制数与人类进制数的差别
计算机进制数指的是
二进制八进制十六进制
人类进制数指的是
十进制
两者差别主要在于十进制数使用 -来表示负数而计算机进制数使用补码来表示负数在其数据本身就包含了符号而十进制数据是不包含符号位的它的符号位是单独存在的。这也是机器数数据符号化的特点。
而人类又习惯于使用十进制这才有了负数的补码转换方法及其硬件设计逻辑。
在这里重点强调不要理会原码、反码这是历史上已经被淘汰的东西他们跟补码只是有数学上的联系以及历史原因。
但是从十进制数转换为补码这一过程跟原码、反码完全没有关系很多教材和老师的讲解都是不正确的。
我们只需要记住以下原则
3.1.1 十进制数转换为补码的原理
请注意这里十进制数的对象为全体实数包含整数和小数他们都适用于补码转换规则重要的一点区别是在计算机存储上
计算机存储整数直接使用补码表示计算机存储小数使用的是浮点表示关于这一点请详细阅读浮点运算的知识
在这里我们以十进制整数为例来进行说明
对于正数这个非常容易直接使用除基取余法即可高位需要用0补全 例如假定寄存器为16位 200D 0000_0000_1100_1000B对于负数需要遵循这样的规则全部取反再加1
例如 -2D
1先表示出2D 0000_0000_0000_0010B
2再全部取反1111_1111_1111_1101B
3再加11111_1111_1111_1110B
4这样得到的结果就是 -2D的补码
需要注意的是此时如果高位有空余需要补上1而不是0
其中我之前提到的取反功能选择器就是完成了取反操作但是还有加1操作呢我们继续看下一小节
3.2 进位输入信号carry_in的作用
我在1.1.1.1节提到过carry_这个信号初始需要置为0加法器单独存在一个的时候它是不太重要的。
只有多个加法器连在一起串行进位第3节会提及的时候上一级的carry_in与下一级的carry_out连接起来才能发挥作用。
但是如果初始将carry_in置为1就能够发挥它的作用了它在补码转换过程起到了加1的作用。
无论输入的数据是1位还是32位加1只需要在最低位加1也就是只需要把单独存在的不与carry_out连接的caryy_in置为1就能实现加1操作。 十进制的20000 0000 0000 0010先取反 1111 1111 1111 1101再加1: 1————————————————————————1111 1111 1111 1110其中取反操作使用取反选择器完成加1操作只需将carry_in置为1这样一来取反选择器与carry_in 1紧密合作就完成了十进制数到补码的转换也就实现了整数的加减法。
备注如果这个部分看不懂请看完第3节再回看此节。
至此我想你能够理解实现整数减法的硬件设计原理了。 思考点
这里留一个问题给读者思考试想如果采用大多数老师讲解的原码、反码和补码的原则去转换补码硬件设计会是怎样的
这里我直接给你结果分析具体设计不再讲解自行完成。 显然按照这样的方式设计会非常复杂而且没有必要既违背了硬件设计简单性原则也增加了数学运算的复杂度
一般老师讲解的原码、反码和补码的转换方式是
先求原码再求反码再求补码
我教你的是
全部取反再加1
这二者是区别就是
前者教你 1 10 - 9 2后者教你 1 1 2
对比之下哪个更加简洁不言而喻事实上这些都是人为规定怎么计算都可以在数学上都说的通但是要考虑硬件简单性原则后者显然更加占优势。 假定a,b均为非负整数则 a - b a ( b取反 1 )
但是事实的情况往往会更加复杂比如-a - b又如何处理
目前从原理上能够实现减法但这远远不够还需要根据实际需求进一步改进这就涉及到了将其添加到MIPS指令上这个以后再说。
3 建模32位加法器
这里采用串行进位方式进行连接并非并行进位超前进位。
关于基础加法器的超前进位实现参照我的文章全面剖析加法器——从1位到4位从一般加法串行进位到快速加法并行进位/超前进位【此处链接待完善】
3.1 科技黑箱外部端口 3.2 揭秘黑箱内部细节 3.2.1 组合式设计的核心原则
全部接口都连接分三大类 与外部总线连接与外部一根线连接内部相互连接配置内部线网一般为数组
4 实现32位加法器 4.1 设计块
此处直接使用前面设计好的1位加法器再次强调此处为串行进位
module add_32sizes (input [31:0] a,b,input a_invert,b_invert,input carry_in,output carry_out,output [31:0] sum);// 设置内部线网wire carry_out_in [30:0]; // 内部串行进位传递// 调用模块实例add_1size ADD0 (a[0],b[0],a_invert,b_invert,carry_in,carry_out_in[0],sum[0]);add_1size ADD1 (a[1],b[1],a_invert,b_invert,carry_out_in[0],carry_out_in[1],sum[1]);add_1size ADD2 (a[2],b[2],a_invert,b_invert,carry_out_in[1],carry_out_in[2],sum[2]);add_1size ADD3 (a[3],b[3],a_invert,b_invert,carry_out_in[2],carry_out_in[3],sum[3]);add_1size ADD4 (a[4],b[4],a_invert,b_invert,carry_out_in[3],carry_out_in[4],sum[4]);add_1size ADD5 (a[5],b[5],a_invert,b_invert,carry_out_in[4],carry_out_in[5],sum[5]);add_1size ADD6 (a[6],b[6],a_invert,b_invert,carry_out_in[5],carry_out_in[6],sum[6]);add_1size ADD7 (a[7],b[7],a_invert,b_invert,carry_out_in[6],carry_out_in[7],sum[7]);add_1size ADD8 (a[8],b[8],a_invert,b_invert,carry_out_in[7],carry_out_in[8],sum[8]);add_1size ADD9 (a[9],b[9],a_invert,b_invert,carry_out_in[8],carry_out_in[9],sum[9]);add_1size ADD10 (a[10],b[10],a_invert,b_invert,carry_out_in[9],carry_out_in[10],sum[10]);add_1size ADD11 (a[11],b[11],a_invert,b_invert,carry_out_in[10],carry_out_in[11],sum[11]); add_1size ADD12 (a[12],b[12],a_invert,b_invert,carry_out_in[11],carry_out_in[12],sum[12]);add_1size ADD13 (a[13],b[13],a_invert,b_invert,carry_out_in[12],carry_out_in[13],sum[13]);add_1size ADD14 (a[14],b[14],a_invert,b_invert,carry_out_in[13],carry_out_in[14],sum[14]);add_1size ADD15 (a[15],b[15],a_invert,b_invert,carry_out_in[14],carry_out_in[15],sum[15]);add_1size ADD16 (a[16],b[16],a_invert,b_invert,carry_out_in[15],carry_out_in[16],sum[16]);add_1size ADD17 (a[17],b[17],a_invert,b_invert,carry_out_in[16],carry_out_in[17],sum[17]);add_1size ADD18 (a[18],b[18],a_invert,b_invert,carry_out_in[17],carry_out_in[18],sum[18]);add_1size ADD19 (a[19],b[19],a_invert,b_invert,carry_out_in[18],carry_out_in[19],sum[19]);add_1size ADD20 (a[20],b[20],a_invert,b_invert,carry_out_in[19],carry_out_in[20],sum[20]);add_1size ADD21 (a[21],b[21],a_invert,b_invert,carry_out_in[20],carry_out_in[21],sum[21]);add_1size ADD22 (a[22],b[22],a_invert,b_invert,carry_out_in[21],carry_out_in[22],sum[22]);add_1size ADD23 (a[23],b[23],a_invert,b_invert,carry_out_in[22],carry_out_in[23],sum[23]);add_1size ADD24 (a[24],b[24],a_invert,b_invert,carry_out_in[23],carry_out_in[24],sum[24]);add_1size ADD25 (a[25],b[25],a_invert,b_invert,carry_out_in[24],carry_out_in[25],sum[25]);add_1size ADD26 (a[26],b[26],a_invert,b_invert,carry_out_in[25],carry_out_in[26],sum[26]);add_1size ADD27 (a[27],b[27],a_invert,b_invert,carry_out_in[26],carry_out_in[27],sum[27]);add_1size ADD28 (a[28],b[28],a_invert,b_invert,carry_out_in[27],carry_out_in[28],sum[28]);add_1size ADD29 (a[29],b[29],a_invert,b_invert,carry_out_in[28],carry_out_in[29],sum[29]);add_1size ADD30 (a[30],b[30],a_invert,b_invert,carry_out_in[29],carry_out_in[30],sum[30]);add_1size ADD31 (a[31],b[31],a_invert,b_invert,carry_out_in[30],carry_out,sum[31]);endmodule4.2 激励块
注意因为在Verilog中reg自动以二进制补码形式存储所以此处验证负数的时候我们假定计算机没有以补码存储这一功能我们假定b -5并且手动设置b的值将b的值设置为5的补码5的补码就是其二进制表示0101B以此来验证5的补码经过全部取反加1后是否变成了-5的补码从而验证了设计的正确性。
如果你没有看懂那么请看完第6节的补充链接再回顾此内容。
module test;reg [31:0] a,b; // 注意reg为无符号数reg a_invert,b_invert;integer carry_in;wire carry_out;wire [31:0] sum;add_32sizes AD0 (a,b,a_invert,b_invert,carry_in,carry_out,sum);// 设置信号假设b -5a 8求a b initialbegina 4b1000; // 8的补码b 4b0101; // reg为无符号数自动把-5存为补码无法验证结果因此使用 |-5| 5 的补码a_invert 0; b_invert 1; // b取反carry_in 1; // b取反加1#1 $display(a %d, b -%d\na b %d\n,a,b,sum); // 模拟 a - bif(sum 3)$display(验证成功 8 (- 5) 3);endendmodule4.2.1 报错解决方案
对于这样的错误[XSIM 43-3238] Failed to link the design.
通常情况下是因为激励块与设计块的连接出了问题可能是
模块调用错误模块忘记实例化模块接口连接错误
可以按照这个思路依次排查
4.3 仿真验证 5 行为级建模的魅力
前面说了那么多其实真正硬件编程的时候没有那么麻烦强大的EDA工具让我们能够只需要描述其行为就能完成设计这是抽象层次足够高的设计让我们来看一下。
这里你可能想要摔电脑了什么我做了这么半天实际上只需要几行那你写了这么多有啥用
冷静一下仔细想一下你是不是在之前的操作中对于加法器已经非常熟悉了如果你认真做了我想应该是的这就是Learning by doing在做中学能够快速掌握知识加深对于知识的理解程度这一点是很必要的因此你必须花时间来完成它。
5.1 设计块
module add_32bits(input [31:0] a,b,input ADDctl, // 控制端用于识别加减法 0代表加法1代表减法output reg [31:0] result);always (a,b,ADDctl)begincase(ADDctl)0: result a b;1: result a - b;endcaseendendmodule5.2 激励块
module test;// reg signed [31:0] a,b;integer a,b;reg ADDctl;wire signed [31:0] result;add_32bits ADD_0 (a,b,ADDctl,result);initialbegin// 测试加法a -1; b 20; ADDctl 0;#1 $display(%d %d %d,a,b,result);#1// 测试减法 【1-20 溢出值因为不识别补码怎么办】a -10; b 28; ADDctl 1;#1 $display(%d - %d %d,a,b,result);endendmodule5.3 结果验证 5.4 思考点
reg类型将负数以补码存储实现了减法变为加法那么Verilog中为何还有减法运算Verilog中负数以补码形式存储令其以%d形式输出的时候并不会显示负数而是显示一个超大的正数如何解决如何正确显示激励块输入的负数输入端口的reg类型可否使用integer类型
以上问题看完下一小结的扩展链接你就能得知答案。
6 由行为级描述掌握几大重要思想
设计的思想有
分治思想归一思想实战验证思维查阅资料的能力
我单独写了一篇文章以阐述一些重要的理论和思想。请看待完善
7 待求解决方案的错误
[XSIM 43-3345] Unable to remove previous simulation file xsim.dir/test_behav/xsimk.exe. Please check if you have another instance of this simulation running on your system, terminate it and then recompile your design. System Error Message: boost::filesystem::status: 拒绝访问。: xsim.dir/test_behav/xsimk.exe.
[XSIM 43-3238] Failed to link the design.
我不知道为什么会拒绝访问并且此状态下系统不能删除文件唯一的办法就是重新建立一个文件并且将设计块和激励块copy过去。
问题待解决中如有读者知道解决方案欢迎私聊我~