【每周FPGA案例】至简设计系列_BCD译码实现
至简设计系列_BCD译码实现【上板现象】BCD译码实现在MP801的上板现象
https://www.bilibili.com/video/BV1Af4y117H4?p=24
BCD译码实现在点拨开发板的上板现象
https://www.bilibili.com/video/BV1Af4y117H4?p=25
BCD译码实现在实验箱的上板现象
https://www.bilibili.com/video/BV1Af4y117H4?p=23
--作者:肖肖肖
本文为明德扬原创及录用文章,转载请注明出处!
1.1 总体设计1.1.1 概述BCD码(Binary-Coded Decimal),用4位二进制数来表示1位十进制数中的0~9这10个数码,是一种二进制的数字编码形式,用二进制编码的十进制代码。BCD码这种编码形式利用了四个位元来储存一个十进制的数码,使二进制和十进制之间的转换得以快捷的进行。
1.1.2 设计目标实现BCD译码并显示十进制结果的程序,具体功能要求如下:1. 串口发送8位十六进制数给FPGA; 2. FPGA接收串口数据并对其进行BCD译码;3. 在数码管上以十进制显示串口发送的数值。
1.1.3 系统结构框图系统结构框图如下图一所示:
图一1.1.4模块功能Ø串口接收模块实现功能1、接收上位机PC发来的位宽为8的十六进制数据。ØBCD译码模块实现功能1、对接收到的8位十六进制数据进行BCD译码。Ø数码管显示模块实现功能1、显示BCD译码后的十进制数值。1.1.5顶层信号
信号名I/O位宽定义
clkI1系统工作时钟 50M
rst_nI1系统复位信号,低电平有效
rx_uartI11bit串口接收数据
SegmentO88位数码管段选信号
Seg_selO33位数码管位选信号
1.1.6参考代码下面是使用工程的顶层代码:module top(
clk ,
rst_n ,
rx_uart ,
seg_sel ,
segment
);
parameter DATA_W = 8;
parameter SEG_WID = 8;
parameter SEL_WID = 3;
parameter BCD_OUT = 12;
input clk ;
input rst_n ;
input rx_uart;
outputseg_sel;
outputsegment;
wire seg_sel;
wire segment;
wirerx_dout ;
wire rx_dout_vld ;
wirebcd_dout ;
wire bcd_dout_vld ;
uart_rx u1(
.clk (clk ),
.rst_n (rst_n ),
.din (rx_uart ),
.dout (rx_dout ),
.dout_vld (rx_dout_vld )
);
bcd_water u2(
.clk (clk ),
.rst_n (rst_n ),
.din (rx_dout ),
.din_vld (rx_dout_vld ),
.dout (bcd_dout ),
.dout_vld (bcd_dout_vld )
);
seg_disp#(.SEG_NUM(SEL_WID)) u3(
.clk (clk ),
.rst_n (rst_n ),
.din (bcd_dout ),
.din_vld (bcd_dout_vld ),
.seg_sel (seg_sel ),
.segment (segment )
);
endmodule
1.2 串口接收模块设计1.2.1接口信号
信号名I/O位宽定义
clkI1系统工作时钟 50M
rst_nI1系统复位信号,低电平有效
dinI11bit串口接收数据
doutO88bit的输出数据
dout_vldO1输出数据有效指示信号
1.2.2 设计思路在前面的案例中已经有串口接收模块的介绍,所以这里不在过多介绍,详细介绍请看下方链接:http://www.fpgabbs.cn/forum.php?mod=viewthread&tid=1074&fromuid=100105
1.2.3参考代码module uart_rx(
clk ,
rst_n ,
din ,
dout ,
dout_vld
);
parameter DATA_W = 8 ;
parameter NUM_W= 4 ;
parameter CNT_W= 14 ;
parameter BPS = 5208 ;
parameter BPS_P= BPS/2;
input clk ;
input rst_n ;
input din ;
outputdout ;
output dout_vld ;
reg dout ;
reg dout_vld ;
reg data_num ;
reg rx_temp_data ;
reg din_ff0 ;
reg din_ff1 ;
reg din_ff2 ;
reg flag_add ;
wire end_cnt ;
wire end_cnt_p ;
wire add_data_num ;
wire end_data_num ;
reg cnt ;
wire add_cnt ;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
din_ff0 <= 1'b1;
din_ff1 <= 1'b1;
din_ff2 <= 1'b1;
end
else begin
din_ff0 <= din;
din_ff1 <= din_ff0;
din_ff2 <= din_ff1;
end
end
always @ (posedge clk or negedge rst_n)begin
if(!rst_n) begin
flag_add <= 1'b0;
end
else if(din_ff2 & ~din_ff1) begin
flag_add <= 1'b1;
end
else if(data_num==4'd8&&end_cnt) begin
flag_add <= 1'b0;
end
end
always @ (posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 0;
end
else begin
cnt <= cnt+1'b1;
end
end
else begin
cnt <= 0;
end
end
assign add_cnt = flag_add;
assign end_cnt = add_cnt && cnt == BPS-1;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
data_num <= 0;
end
else if(add_data_num) begin
if(end_data_num)
data_num <= 0;
else
data_num <= data_num+1 ;
end
end
assign add_data_num = end_cnt;
assign end_data_num = add_data_num&& data_num == 9-1 ;
always @ (posedge clk or negedge rst_n)begin
if(!rst_n) begin
dout <= 8'd0;
end
else if(add_cnt && cnt==BPS_P-1 && data_num!=0) begin
dout<={din,{dout}};
end
else begin
dout<=dout;
end
end
always @ (posedge clk or negedge rst_n)begin
if(!rst_n) begin
dout_vld <= 1'b0;
end
else if(add_data_num && data_num == 4'd8) begin
dout_vld <= 1'b1;
end
else begin
dout_vld <= 1'b0;
end
end
endmodule
1.3 BCD译码模块设计1.3.1接口信号
信号名I/O位宽定义
clkI1系统工作时钟 50M
rst_nI1系统复位信号,低电平有效
dinI88bit的输入数据
din_vldI1输入数据有效指示信号
doutO1212bit的输出BCD译码数据,每 4 bit一组,分别表示百、十、个位的值
dout_vldO1输出数据有效指示信号
1.3.2设计思路Ø左移加3算法此处二进制转 BCD 码的硬件实现,采用左移加 3 的算法,具体描述如下:(此处以 8-bit 二进制码为例)1、左移要转换的二进制码 1 位2、左移之后,BCD 码分别置于百位、十位、个位3、如果移位后所在的 BCD 码列大于或等于 5,则对该值加 34、继续左移的过程直至全部移位完成
举例:将十六进制码 0xFF 转换成 BCD 码
1.3.3参考代码module bcd_water(
clk ,
rst_n ,
din ,
din_vld ,
dout ,
dout_vld
);
input clk ;
input rst_n ;
input [ 7:0] din ;
input din_vld ;
output dout ;
wire dout ;
output dout_vld ;
wire dout_vld ;
reg din_temp ;
reg din_temp_ff0 ;
reg din_temp_ff1 ;
reg din_temp_ff2 ;
reg din_temp_ff3 ;
reg din_temp_ff4 ;
wire din_shift_temp ;
wire din_shift_temp_ff0;
wire din_shift_temp_ff1;
wire din_shift_temp_ff2;
wire din_shift_temp_ff3;
wire [ 7:0] din_a_temp ;
wire [ 3:0] din_b_temp ;
wire [ 3:0] din_c_temp ;
wire [ 3:0] din_d_temp ;
wire [ 7:0] din_add_a_temp ;
wire [ 3:0] din_add_b_temp ;
wire [ 3:0] din_add_c_temp ;
wire [ 3:0] din_add_d_temp ;
wire [ 7:0] din_a_temp_ff0 ;
wire [ 3:0] din_b_temp_ff0 ;
wire [ 3:0] din_c_temp_ff0 ;
wire [ 3:0] din_d_temp_ff0 ;
wire [ 7:0] din_a_temp_ff1 ;
wire [ 3:0] din_b_temp_ff1 ;
wire [ 3:0] din_c_temp_ff1 ;
wire [ 3:0] din_d_temp_ff1 ;
wire [ 7:0] din_a_temp_ff2 ;
wire [ 3:0] din_b_temp_ff2 ;
wire [ 3:0] din_c_temp_ff2 ;
wire [ 3:0] din_d_temp_ff2 ;
wire [ 7:0] din_a_temp_ff3 ;
wire [ 3:0] din_b_temp_ff3 ;
wire [ 3:0] din_c_temp_ff3 ;
wire [ 3:0] din_d_temp_ff3 ;
wire [ 7:0] din_add_a_temp_ff0;
wire [ 3:0] din_add_b_temp_ff0;
wire [ 3:0] din_add_c_temp_ff0;
wire [ 3:0] din_add_d_temp_ff0;
wire [ 7:0] din_add_a_temp_ff1;
wire [ 3:0] din_add_b_temp_ff1;
wire [ 3:0] din_add_c_temp_ff1;
wire [ 3:0] din_add_d_temp_ff1;
wire [ 7:0] din_add_a_temp_ff2;
wire [ 3:0] din_add_b_temp_ff2;
wire [ 3:0] din_add_c_temp_ff2;
wire [ 3:0] din_add_d_temp_ff2;
wire [ 7:0] din_add_a_temp_ff3;
wire [ 3:0] din_add_b_temp_ff3;
wire [ 3:0] din_add_c_temp_ff3;
wire [ 3:0] din_add_d_temp_ff3;
reg dout_vld_temp ;
reg dout_vld_temp_ff0 ;
reg dout_vld_temp_ff1 ;
reg dout_vld_temp_ff2 ;
reg dout_vld_temp_ff3 ;
reg dout_vld_temp_ff4 ;
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
din_temp <= 20'b0;
end
else if(din_vld)begin
din_temp <= {9'b0,din,3'b0};
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dout_vld_temp <= 1'b0;
end
else if(din_vld)begin
dout_vld_temp <= 1'b1;
end
else begin
dout_vld_temp <= 1'b0;
end
end
assigndin_a_temp = din_temp[ 7: 0] ;
assigndin_b_temp = din_temp ;
assigndin_c_temp = din_temp ;
assigndin_d_temp = din_temp ;
assigndin_add_a_temp = din_a_temp ;
assigndin_add_b_temp = din_b_temp + ((din_b_temp>=5)?4'd3:4'd0) ;
assigndin_add_c_temp = din_c_temp + ((din_c_temp>=5)?4'd3:4'd0) ;
assigndin_add_d_temp = din_d_temp + ((din_d_temp>=5)?4'd3:4'd0) ;
assigndin_shift_temp = {din_add_d_temp,din_add_c_temp,din_add_b_temp,din_add_a_temp,1'b0};
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
din_temp_ff0 <= 20'b0;
end
else begin
din_temp_ff0 <= din_shift_temp;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dout_vld_temp_ff0 <= 1'b0 ;
end
else begin
dout_vld_temp_ff0 <= dout_vld_temp;
end
end
assigndin_a_temp_ff0 = din_temp_ff0[ 7: 0] ;
assigndin_b_temp_ff0 = din_temp_ff0 ;
assigndin_c_temp_ff0 = din_temp_ff0 ;
assigndin_d_temp_ff0 = din_temp_ff0 ;
assigndin_add_a_temp_ff0 = din_a_temp_ff0 ;
assigndin_add_b_temp_ff0 = din_b_temp_ff0 + ((din_b_temp_ff0>=5)?4'd3:4'd0) ;
assigndin_add_c_temp_ff0 = din_c_temp_ff0 + ((din_c_temp_ff0>=5)?4'd3:4'd0) ;
assigndin_add_d_temp_ff0 = din_d_temp_ff0 + ((din_d_temp_ff0>=5)?4'd3:4'd0) ;
assigndin_shift_temp_ff0 = {din_add_d_temp_ff0,din_add_c_temp_ff0,din_add_b_temp_ff0,din_add_a_temp_ff0,1'b0};
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
din_temp_ff1 <= 20'b0;
end
else begin
din_temp_ff1 <= din_shift_temp_ff0;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dout_vld_temp_ff1 <= 1'b0;
end
else begin
dout_vld_temp_ff1 <= dout_vld_temp_ff0;
end
end
assigndin_a_temp_ff1 = din_temp_ff1[ 7: 0] ;
assigndin_b_temp_ff1 = din_temp_ff1 ;
assigndin_c_temp_ff1 = din_temp_ff1 ;
assigndin_d_temp_ff1 = din_temp_ff1 ;
assigndin_add_a_temp_ff1 = din_a_temp_ff1 ;
assigndin_add_b_temp_ff1 = din_b_temp_ff1 + ((din_b_temp_ff1>=5)?4'd3:4'd0) ;
assigndin_add_c_temp_ff1 = din_c_temp_ff1 + ((din_c_temp_ff1>=5)?4'd3:4'd0) ;
assigndin_add_d_temp_ff1 = din_d_temp_ff1 + ((din_d_temp_ff1>=5)?4'd3:4'd0) ;
assigndin_shift_temp_ff1 = {din_add_d_temp_ff1,din_add_c_temp_ff1,din_add_b_temp_ff1,din_add_a_temp_ff1,1'b0};
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
din_temp_ff2 <= 20'b0;
end
else begin
din_temp_ff2 <= din_shift_temp_ff1;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dout_vld_temp_ff2 <= 1'b0;
end
else begin
dout_vld_temp_ff2 <= dout_vld_temp_ff1;
end
end
assigndin_a_temp_ff2 = din_temp_ff2[ 7: 0] ;
assigndin_b_temp_ff2 = din_temp_ff2 ;
assigndin_c_temp_ff2 = din_temp_ff2 ;
assigndin_d_temp_ff2 = din_temp_ff2 ;
assigndin_add_a_temp_ff2 = din_a_temp_ff2 ;
assigndin_add_b_temp_ff2 = din_b_temp_ff2 + ((din_b_temp_ff2>=5)?4'd3:4'd0) ;
assigndin_add_c_temp_ff2 = din_c_temp_ff2 + ((din_c_temp_ff2>=5)?4'd3:4'd0) ;
assigndin_add_d_temp_ff2 = din_d_temp_ff2 + ((din_d_temp_ff2>=5)?4'd3:4'd0) ;
assigndin_shift_temp_ff2 = {din_add_d_temp_ff2,din_add_c_temp_ff2,din_add_b_temp_ff2,din_add_a_temp_ff2,1'b0};
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
din_temp_ff3 <= 20'b0;
end
else begin
din_temp_ff3 <= din_shift_temp_ff2;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dout_vld_temp_ff3 <= 1'b0;
end
else begin
dout_vld_temp_ff3 <= dout_vld_temp_ff2;
end
end
assigndin_a_temp_ff3 = din_temp_ff3[ 7: 0] ;
assigndin_b_temp_ff3 = din_temp_ff3 ;
assigndin_c_temp_ff3 = din_temp_ff3 ;
assigndin_d_temp_ff3 = din_temp_ff3 ;
assigndin_add_a_temp_ff3 = din_a_temp_ff3 ;
assigndin_add_b_temp_ff3 = din_b_temp_ff3 + ((din_b_temp_ff3>=5)?4'd3:4'd0) ;
assigndin_add_c_temp_ff3 = din_c_temp_ff3 + ((din_c_temp_ff3>=5)?4'd3:4'd0) ;
assigndin_add_d_temp_ff3 = din_d_temp_ff3 + ((din_d_temp_ff3>=5)?4'd3:4'd0) ;
assigndin_shift_temp_ff3 = {din_add_d_temp_ff3,din_add_c_temp_ff3,din_add_b_temp_ff3,din_add_a_temp_ff3,1'b0};
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
din_temp_ff4 <= 20'b0;
end
else begin
din_temp_ff4 <= din_shift_temp_ff3;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dout_vld_temp_ff4 <= 1'b0;
end
else begin
dout_vld_temp_ff4 <= dout_vld_temp_ff3;
end
end
assigndout = din_temp_ff4;
assigndout_vld = dout_vld_temp_ff4 ;
endmodule
1.4 数码管显示模块设计1.4.1接口信号
信号名I/O位宽定义
clkI1系统工作时钟 50M
rst_nI1系统复位信号,低电平有效
dinI1212位的输入BCD译码数据
din_vldI1输入数据有效指示信号
segmentO88位数码管段选信号
seg_selO33位数码管位选信号
1.4.2设计思路在前面的案例中已经有数码管显示的介绍,所以这里不在过多介绍,详细介绍请看下方链接:http://fpgabbs.com/forum.php?mod=viewthread&tid=399
1.4.3参考代码moduleseg_disp(
rst_n ,
clk ,
din ,
din_vld ,
seg_sel ,
segment
);
parameterDATA_IN = 12 ;
parameterTIME_30MS = 15000 ;
parameterSEG_WID = 8 ;
parameterSEG_NUM = 8 ;
parameterCNT_WID = 10 ;
parameterNUM_0 = 8'b1100_0000;
parameterNUM_1 = 8'b1111_1001;
parameterNUM_2 = 8'b1010_0100;
parameterNUM_3 = 8'b1011_0000;
parameterNUM_4 = 8'b1001_1001;
parameterNUM_5 = 8'b1001_0010;
parameterNUM_6 = 8'b1000_0010;
parameterNUM_7 = 8'b1111_1000;
parameterNUM_8 = 8'b1000_0000;
parameterNUM_9 = 8'b1001_0000;
parameterNUM_ERR = 8'b1111_1111;
input clk ;
input rst_n ;
input din ;
input din_vld ;
output seg_sel ;
output segment ;
reg seg_sel ;
reg segment ;
reg [ 31 : 0] cnt_30us ;
reg sel_cnt ;
reg [ 4 - 1 : 0] seg_tmp ;
wire add_cnt_30us;
wire end_cnt_30us;
wire add_sel_cnt ;
wire end_sel_cnt ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_30us <= 0;
end
else if(add_cnt_30us) begin
if(end_cnt_30us)
cnt_30us <= 0;
else
cnt_30us <= cnt_30us+1 ;
end
end
assign add_cnt_30us = 1;
assign end_cnt_30us = add_cnt_30us&& cnt_30us == TIME_30MS-1 ;
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)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_cnt_30us;
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
seg_tmp <= 0;
end
else begin
seg_tmp <= din;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
segment<=NUM_0;
end
elsebegin
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;
default : segment <= NUM_ERR;
endcase
end
end
endmodule
1.5 效果和总结
Ø下图是该工程在db603开发板上的现象——串口发送数据8’h93,数码管显示12’d147。
Ø下图是该工程在mp801试验箱上的现象——串口发送数据8’he9,数码管显示12’d233。
Ø下图是该工程在ms980试验箱上的现象——串口发送数据8’h52,数码管显示12’d082。
由于该项目的上板现象是串口发送8位的十六进制数经过BCD译码后,在数码管上显示对应的十进制数值,想观看完整现象的朋友可以看一下上板演示的视频。感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也可以看一下我们往期的文章:《基于FPGA的密码锁设计》《波形相位频率可调DDS信号发生器》《基于FPGA的曼彻斯特编码解码设计》《基于FPGA的出租车计费系统》《数电基础与Verilog设计》《基于FPGA的频率、电压测量》《基于FPGA的汉明码编码解码设计》《关于锁存器问题的讨论》《阻塞赋值与非阻塞赋值》《参数例化时自动计算位宽的解决办法》
1.6 公司简介明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。点拨开发板——学习FPGA的入门之选。
MP801开发板——千兆网、ADDA、大容量SDRAM等,学习和项目需求一步到位。网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA。周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO架构设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。项目承接——承接企业FPGA研发项目。人才服务——提供人才推荐、人才代培、人才派遣等服务。
【设计教程下载】
【设计视频教程】
https://www.bilibili.com/video/BV1Af4y117H4?p=33
【工程源码】
谢谢,为什么不能下载 请打电话找我138 发表于 2021-8-18 12:01
谢谢,为什么不能下载
经测试,可以正常下载的。需要登陆才能下载
页:
[1]