用fpga怎么设计乘法器?每周FPGA案例至简设计系列
用fpga怎么设计乘法器?每周FPGA案例至简设计系列2、 当算式输入完成按下等号之后,如果运算符是“减”,并且运算数1小于运算数2,则运算结果为负数,将result_neg信号拉高。1、 只有当运算结果为负数的时候,才显示“负号”,因此初始状态为低电平;1.7.2设计思路本模块的作用是根据运算符,对运算数1和运算数2进行操作得出结果。由于再进行计算的时候考虑小数减去大数的情况,所以运算结果允许为负数,因此需要有符号位指示信号,下面是运算结果符号位指示信号result_neg的设计思路:
接(一)
1.7 运算单元模块设计
1.7.1接口信号
1.7.2设计思路
本模块的作用是根据运算符,对运算数1和运算数2进行操作得出结果。
由于再进行计算的时候考虑小数减去大数的情况,所以运算结果允许为负数,因此需要有符号位指示信号,下面是运算结果符号位指示信号result_neg的设计思路:
1、 只有当运算结果为负数的时候,才显示“负号”,因此初始状态为低电平;
2、 当算式输入完成按下等号之后,如果运算符是“减”,并且运算数1小于运算数2,则运算结果为负数,将result_neg信号拉高。
3、 由于该计算器支持连续输入,如果当前计算的结果为负数,接着输入的运算符为“加”,下一次进行加法运算,并且运算数1(此时比较不考虑符号位)小于或等于运算数2,则表示运算结果为正数,此时将result_neg信号拉低。
4、 在进行连续计算的时候,如果得到的结果超过显示上限,要进入错误状态,这个时候符号位指示信号应该为低电平。
5、 无论在计算中得到的结果是正还是负,如果下次输入的为运算数1,都需要result_neg信号为低电平。
6、 由于除法不支持小数显示,只取整数部分,所以当运算结果为负数,并进行除法运算的时候,如果得到的结果为0,不应该显示为“负0”,应当将符号位指示信号置为低电平。
本模块主要的功能是实现加减乘除运算,下面是对运算结果输出信号result的设计思路:
1、 初始状态没有经过计算,自然输出为0。
2、 在进行加法的时候,由于存在连续计算的情况,需要考虑符号位。当符号位指示信号为0,直接将运算数1和运算数2相加即可;当符号位指示信号为1,则需要判断运算数1和运算数2的大小,确保是大的减去小的。
3、 在进行减法的时候,同样需要考虑符号位。当符号位指示信号为0的时候,需要判断运算数1和运算数2的大小,保证大的减去小的;当符号位指示信号位1的时候,直接将运算数1和运算数2相加即可。
4、 乘法运算直接将运算数1和运算数2相乘即可。
5、 在进行除法运算时,由于无法表示小数,因此这里需要采用运算数1除以运算数2取整的方法,即op_1/op_2。
在计算过程中,如果得到的结果超过显示上限或者错误的计算方法,需要做出错误提示,下面是对于运算结果错误信号result_err的设计思路:
1、 初始状态下,该信号为0,表示没有错误。
2、 得到运算结果后,若继续输入数字,则会进入到运算数1状态,这个时候不进行错误提示。
3、 在运算数2状态下输入运算符,或者在结果不是错误的状态下输出“等号”(表示进行连续计算)。根据输入的运算符进行相应的判断:
加:如果运算结果为正数,则判断运算数1加上运算数2之后会不会溢出,若溢出则做出错误提示;如果运算结果为负数,则不进行错误提示。
减:如果运算结果为负数,则判断运算数1加上运算数2之后会不会溢出,若溢出则做出错误提示;如果运算结果为正数,则判断两个数相减之后的结果是否会溢出。
乘:无论运算结果为何值,都只需要判断两数相乘之后的的结果会不会溢出就可以了。
除:在进行除法运算的时候,需要避免出现除数为0的情况,如果出现此情况,则进行错误指示。
1.7.3参考代码
assignkey_op_en = (key_num==10 || key_num==11 || key_num==12 || key_num==13) && key_vld==1;
assignkey_cal_en = key_num==15 && key_vld==1;
assigncalculate = (state_c_ff==OP_2 && state_c==OPER || key_cal_en==1);
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
state_c_ff <= 0;
end
else begin
state_c_ff <= state_c;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
result <= 0;
end
else if(calculate==1)begin
case(oper)
ADD:begin
if(result_neg==0)
result <= op_1 op_2;
else
result <= (op_1>op_2) ? (op_1 - op_2) : (op_2 - op_1);
end
DEV:begin
if(result_neg==0)
result <= (op_1>op_2) ? (op_1 - op_2) : (op_2 - op_1);
else
result <= op_1 op_2;
end
MUL:begin
result <= op_1 * op_2;
end
DIV:begin
result <= op_1 / op_2;
end
default:result <= op_1;
endcase
end
else begin
result <= result;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
result_neg <= 0;
end
else if(state_c==OP_1)begin
result_neg <= 1'b0;
end
else if(state_c==ERROR)begin
result_neg <= 1'b0;
end
else if(calculate==1 && oper==DEV && op_1<op_2)begin
result_neg <= 1'b1;
end
else if(calculate==1 && result_neg==1 && oper==ADD && op_1<=op_2)begin
result_neg <= 1'b0;
end
else if(result==0)begin
result_neg <= 1'b0;
end
else begin
result_neg <= result_neg;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
result_err <= 0;
end
else if(state_c==OP_1)begin
result_err <= 1'b0;
end
else if((state_c_ff==OP_2 && state_c==OPER) || (key_cal_en==1 && state_c_ff!=ERROR))begin
case(oper)
ADD:begin
if(result_neg==0)
result_err <= (op_1 op_2)>9999_9999 ? 1'b1 : 1'b0;
else
result_err <= 1'b0;
end
DEV:begin
if(result_neg==1)
result_err <= (op_1 op_2)>999_9999 ? 1'b1 : 1'b0;
else if(op_2>op_1)
result_err <= (op_2-op_1)>999_9999 ? 1'b1 : 1'b0;
else
result_err <= 1'b0;
end
MUL:begin
if(result_neg==1)
result_err <= (op_1*op_2)>999_9999 ? 1'b1 : 1'b0;
else
result_err <= (op_1*op_2)>9999_9999 ? 1'b1 : 1'b0;
end
DIV:begin
if(op_2==0)
result_err <= 1'b1;
else
result_err <= 1'b0;
end
default:result_err <= 1'b0;
endcase
end
else begin
result_err <= 1'b0;
end
end
请跳转(二)继续学习。
1.8 显示对象选择模块设计
1.8.1接口信号
1.8.2设计思路
该模块的作用是根据当前计算器的工作状态来选择数码管的显示内容。
1、 复位后,该模块输出0;
2、 当计算器处于OP_1状态下,该模块选择输出运算数1。
3、 当计算器处于OPER状态下,该模块选择输出运算数1。
4、 当计算器处于OP_2状态下,该模块选择输出运算数2。
5、 当计算器处于RESULT状态下,该模块选择输出运算数1。
6、 当计算器处于ERROR状态下,该模块选择输出8个F。
要将数据送到数码管显示,需要将收到的数据进行拆分,比如输入进来的是“12”,需要拆成一个4bit的“1”和一个4bit的“2”送给数码管显示模块。因此设计一个计数器的架构,如下图所示:
架构中使用到了一个时钟计数器dis_cnt、一个采集状态指示信号flag_add、dis_sel为输入要显示的数据、dis_sel_tmp为输入数据打一拍之后的数据、result_neg为运算结果符号位指示信号、result_neg_tmp为运算结果符号位指示信号打一拍之后的信号。下面分别介绍一下这些信号的设计思路:
采集状态指示信号flag_add:初始状态为0,表示不对数据进行采集显示。如果检测到输入的数据或者符号位发生变化,表示要在数码管上显示的数据有变化,该信号拉高,计数器可以进行计数,所以由0变1的条件为dis_sel!=dis_sel_tmp || result_neg!=result_neg_tmp。当计数器数完之后,表示要显示的数据已经全部显示,则将此信号拉低,所以由1变0的条件是end_dis_cnt。
显示数据dis_sel:该信号根据工作状态进行选择,当目前处于OP_2状态时,选择运算数2输入数据,其他情况都选择运算数1输入数据。
显示数据打一拍之后的信号dis_sel_tmp:该信号存在的目的就是为了检测显示数据是否发生变化。
运算结果符号位指示信号result_neg:输入信号。
符号位指示信号打一拍之后的信号result_neg_tmp:该信号存在的意义就是为了检测符号位是否发生变化。
时钟计数器dis_cnt:该计数器的作用有两个,延时和控制输入数据赋值给显示数据输出信号的对应位。加一条件为flag_add && (dis_sel==dis_sel_tmp &&result_neg==result_neg_tmp),表示处在采集状态时,如果显示数据和符号位指示信号稳定,则开始计数。结束条件为数10个,由于计数器刚开始计数的时候,显示数据存在变化的可能,因此这里选择延迟两个时钟在对显示数据输出信号进行赋值(由于后面数据都是保持不变的,因此这个延时时间不是固定的,可以多延时一些),共有8个数码管,因此要赋值8次,所以计数器共需要数10个。
前面提到过,需要将显示数据显示到数码管上的话,需要将每一个数字进行拆分,一般采用除以10取余和取整的方法,本工程使用除法器的IP核,该IP核的作用就是将输入的数据除以10,得到商和余数。
生成过程如下:
第一步、使用软件为Quartus Prime LiteEdition 18.1版本。首先打开软件之后,在主页面的右边找到“IP Catalog”窗口,在搜索栏中输入“div”进行搜索,然后双击“LPM_DIVIDE”。如果没有找到“IP Catalog”窗口,可在上方工具栏“Tools”中选择“IP Catalog”调出。
第二步、选择IP核生成的路径,并将其命名为“div”,注意这里的名字不能有中文字符或者全数字。在下方文件类型中选择“Verilog”,然后点击OK。
第三步、在之后出现的IP核设置界面中,“How wide should the numerator input”表示需要设置的分子的位宽,这里设置为27。“How wide should thedenominator input”表示需要设置的分母的位宽这里设置为4。在下方分子和分母的表示都选用“Unsigned”无符号类型。然后点击Next
第四步、下图中的1处表示是否需要对输出进行打拍,这里选择打一拍之后输出。2处表示要进行的优化,这里选择默认优化。3处表示是否总是返回正余数,选择是。然后点击Next。
第五步、方框出表示该IP核在仿真的时候需要调用的库,直接点击Next即可。
第六步、这一界面是设置需要生成的文件,本工程只需要生成默认的即可,所以不用勾选。点击Finish。
1.8.3参考代码
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
result_neg_tmp <= 0;
end
else begin
result_neg_tmp <= result_neg;
end
end
always@(*)begin
if(state_c==OP_2)begin
dis_sel = op_2;
end
else begin
dis_sel = op_1;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dis_sel_tmp <= 0;
end
else begin
dis_sel_tmp <= dis_sel;
end
end
div div_prj(
.clock (clk )
.numer (dis_tmp )
.denom (10 )
.quotient (div_quo )
.remain (div_rem )
);
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
flag_add <= 0;
end
else if(dis_sel!=dis_sel_tmp || result_neg!=result_neg_tmp)begin
flag_add <= 1;
end
else if(end_dis_cnt)begin
flag_add <= 0;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
dis_cnt <= 0;
end
else if(add_dis_cnt) begin
if(end_dis_cnt)
dis_cnt <= 0;
else
dis_cnt <= dis_cnt 1 ;
end
end
assign add_dis_cnt = flag_add && (dis_sel==dis_sel_tmp && result_neg==result_neg_tmp);
assign end_dis_cnt = add_dis_cnt&& dis_cnt == 10-1 ;
assigndis_tmp = add_dis_cnt && dis_cnt==1 ? dis_sel : div_quo;
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
display <= 4'b0;
end
else if(state_c==ERROR)begin
display[4*(dis_cnt)-1 -:4] <= 4'b1111;
end
else if(end_dis_cnt && result_neg==1 && state_c!=OP_2)begin
display[31:28] <= 4'b1010;
end
else begin
display[4*(dis_cnt-1)-1 -:4] <= div_rem;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
display_vld <= 0;
end
else begin
display_vld <= (dis_cnt==0 && (dis_sel==dis_sel_tmp)) ? 1'b1 : 1'b0;
end
end
1.9 数码管显示模块设计
1.9.1接口信号
1.9.2设计思路
本模块主要实现的功能是对显示对象选择模块的显示数据输出信号(display)进行数码管显示。
1、 复位后,数码管默认显示运算数1;
2、 当result_err有效时,数码管显示8个F;
3、 当result_neg有效时,第8个数码管显示“—”;
4、 数码管显示display;
由于数码管显示在前面已有案例介绍,所以这个就不做介绍。感兴趣的同学可以看一下往期的文章:【每周FPGA案例】至简设计系列_7段数码管显示
1.9.3参考代码
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
count_20us <= 0;
end
else if(add_count_20us) begin
if(end_count_20us)
count_20us <= 0;
else
count_20us <= count_20us 1 ;
end
end
assign add_count_20us = 1;
assign end_count_20us = add_count_20us&& count_20us == TIME_20US-1 ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
sel_cnt <= 0;
end
else if(add_sel_cnt) begin
if(end_sel_cnt)
sel_cnt <= 0;
else
sel_cnt <= sel_cnt 1 ;
end
end
assign add_sel_cnt = end_count_20us;
assign end_sel_cnt = add_sel_cnt&& sel_cnt == SEG_NUM-1 ;
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
seg_sel <= {SEG_NUM{1'b1}};
end
else begin
seg_sel <= ~(1'b1 << sel_cnt);
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
display_ff0 <= 0;
end
else begin
for(ii=0;ii<SEG_NUM;ii=ii 1)begin
if(display_vld==1)begin
display_ff0[(ii 1)*4-1 -:4] <= display[(ii 1)*4-1 -:4];
end
else begin
display_ff0[(ii 1)*4-1 -:4] <= display_ff0[(ii 1)*4-1 -:4];
end
end
end
end
always@(*)begin
seg_tmp = display_ff0[(sel_cnt 1)*4-1 -:4];
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
segment <= NUM_0;
end
else begin
case(seg_tmp)
0 :segment <=NUM_0;
1 :segment <=NUM_1;
2 :segment <=NUM_2;
3 :segment <=NUM_3;
4 :segment <=NUM_4;
5 :segment <=NUM_5;
6 :segment <=NUM_6;
7 :segment <=NUM_7;
8 :segment <=NUM_8;
9 :segment <=NUM_9;
10:segment <=NUM_10 ;
default:segment <= NUM_ERR;
endcase
end
end
1.10 蜂鸣器模块设计
1.10.1接口信号
1.10.2设计思路
该模块的主要功能是根据接收到的各个错误指示信号,进行报警提示。当接收到错误信号有效的时候,蜂鸣器报警,持续1秒的时间,因此提出一个计数器的架构,如下图所示:
主要由时钟计数器cnt_1s和蜂鸣器输出组成,下面是两个信号的设计思路:
时钟计数器cnt_1s:该计数器的作用是计时1秒的时间。加一条件为flag_add,表示进入报警状态的时候便开始计数。结束条件为数5000_0000个,系统时钟为50M,一个时钟周期为20ns,5000_0000个时钟周期就是1秒。
蜂鸣器输出信号beep:初始状态为1,表示不报警。从1变0的条件为op_1_err || op_2_err ||result_err,表示接收到这些错误指示信号之后,开始报警。从0变1的条件为end_cnt_1s,表示报警时间持续1秒,之后结束。
1.10.3参考代码
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_1s <= 0;
end
else if(add_cnt_1s) begin
if(end_cnt_1s)
cnt_1s <= 0;
else
cnt_1s <= cnt_1s 1 ;
end
end
assign add_cnt_1s = beep==0;
assign end_cnt_1s = add_cnt_1s&& cnt_1s == CNT_1S-1 ;
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
beep <= 1'b1;
end
else if(op_1_err || op_2_err || result_err)begin
beep <= 1'b0;
end
else if(end_cnt_1s)begin
beep <= 1'b1;
end
end
1.11 效果和总结
1.11.1db603开发板
由于计算器的演示是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。
1.11.2ms980试验箱
由于计算器的演示是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。
本案例提供教学视频和工程源代码,需要的同学请到明德扬论坛进行学习。
感兴趣的朋友也可以访问明德扬论坛(http://www.FPGAbbs.cn/)进行FPGA相关工程设计学习,也可以看一下我们往期的文章:
《基于FPGA的密码锁设计》
《波形相位频率可调DDS信号发生器》
《基于FPGA的曼彻斯特编码解码设计》
《基于FPGA的出租车计费系统》
《数电基础与Verilog设计》
《基于FPGA的频率、电压测量》
《基于FPGA的汉明码编码解码设计》
《关于锁存器问题的讨论》
《阻塞赋值与非阻塞赋值》
《参数例化时自动计算位宽的解决办法》
明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。点拨开发板——学习FPGA的入门之选。
MP801开发板——千兆网、ADDA、大容量SDRAM等,学习和项目需求一步到位。网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA。周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO架构设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。项目承接——承接企业FPGA研发项目。人才服务——提供人才推荐、人才代培、人才派遣等服务。