本文为明德扬原创及录用文章,转载请注明出处!
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的十六进制数据。 1、 对接收到的8位十六进制数据进行BCD译码。 1、 显示BCD译码后的十进制数值。
1.1.5顶层信号
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 ; output[SEL_WID-1:0] seg_sel ; output[SEG_WID-1:0] segment ; wire [SEL_WID-1:0] seg_sel ; wire [SEG_WID-1:0] segment ; wire [DATA_W-1 :0] rx_dout ; wire rx_dout_vld ; wire [BCD_OUT-1:0] bcd_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接口信号
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 ;output[DATA_W-1:0] dout ; output dout_vld ;reg [DATA_W-1:0] dout ;reg dout_vld ;reg [NUM_W-1 :0] data_num ;reg [DATA_W-1:0] 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_W-1:0] 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; endendalways @ (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; endendalways @ (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; endendassign 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 ; endendassign 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[7:1]}}; end else begin dout<=dout; endendalways @ (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; endendendmodule
1.3 BCD译码模块设计 1.3.1接口信号
1.3.2设计思路 此处二进制转 BCD 码的硬件实现,采用左移加 3 的算法,具体描述如下:(此处以 8-bit 二进制码为例) 1、左移要转换的二进制码 1 位 2、左移之后,BCD 码分别置于百位、十位、个位 3、如果移位后所在的 BCD 码列大于或等于 5,则对该值加 3 4、继续左移的过程直至全部移位完成 举例:将十六进制码 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 [11:0] dout ;wire [11:0] dout ;output dout_vld ;wire dout_vld ;reg [19:0] din_temp ;reg [19:0] din_temp_ff0 ;reg [19:0] din_temp_ff1 ;reg [19:0] din_temp_ff2 ;reg [19:0] din_temp_ff3 ;reg [19:0] din_temp_ff4 ;wire [20:0] din_shift_temp ;wire [20:0] din_shift_temp_ff0 ;wire [20:0] din_shift_temp_ff1 ;wire [20:0] din_shift_temp_ff2 ;wire [20:0] 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}; endendalways @(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; endendassign din_a_temp = din_temp[ 7: 0] ;assign din_b_temp = din_temp[11: 8] ;assign din_c_temp = din_temp[15:12] ;assign din_d_temp = din_temp[19:16] ;assign din_add_a_temp = din_a_temp ;assign din_add_b_temp = din_b_temp + ((din_b_temp>=5)?4'd3:4'd0) ;assign din_add_c_temp = din_c_temp + ((din_c_temp>=5)?4'd3:4'd0) ;assign din_add_d_temp = din_d_temp + ((din_d_temp>=5)?4'd3:4'd0) ;assign din_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[19:0]; endendalways @(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; endendassign din_a_temp_ff0 = din_temp_ff0[ 7: 0] ;assign din_b_temp_ff0 = din_temp_ff0[11: 8] ;assign din_c_temp_ff0 = din_temp_ff0[15:12] ;assign din_d_temp_ff0 = din_temp_ff0[19:16] ;assign din_add_a_temp_ff0 = din_a_temp_ff0 ;assign din_add_b_temp_ff0 = din_b_temp_ff0 + ((din_b_temp_ff0>=5)?4'd3:4'd0) ;assign din_add_c_temp_ff0 = din_c_temp_ff0 + ((din_c_temp_ff0>=5)?4'd3:4'd0) ;assign din_add_d_temp_ff0 = din_d_temp_ff0 + ((din_d_temp_ff0>=5)?4'd3:4'd0) ;assign din_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[19:0]; endendalways @(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; endendassign din_a_temp_ff1 = din_temp_ff1[ 7: 0] ;assign din_b_temp_ff1 = din_temp_ff1[11: 8] ;assign din_c_temp_ff1 = din_temp_ff1[15:12] ;assign din_d_temp_ff1 = din_temp_ff1[19:16] ;assign din_add_a_temp_ff1 = din_a_temp_ff1 ;assign din_add_b_temp_ff1 = din_b_temp_ff1 + ((din_b_temp_ff1>=5)?4'd3:4'd0) ;assign din_add_c_temp_ff1 = din_c_temp_ff1 + ((din_c_temp_ff1>=5)?4'd3:4'd0) ;assign din_add_d_temp_ff1 = din_d_temp_ff1 + ((din_d_temp_ff1>=5)?4'd3:4'd0) ;assign din_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[19:0]; endendalways @(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; endendassign din_a_temp_ff2 = din_temp_ff2[ 7: 0] ;assign din_b_temp_ff2 = din_temp_ff2[11: 8] ;assign din_c_temp_ff2 = din_temp_ff2[15:12] ;assign din_d_temp_ff2 = din_temp_ff2[19:16] ;assign din_add_a_temp_ff2 = din_a_temp_ff2 ;assign din_add_b_temp_ff2 = din_b_temp_ff2 + ((din_b_temp_ff2>=5)?4'd3:4'd0) ;assign din_add_c_temp_ff2 = din_c_temp_ff2 + ((din_c_temp_ff2>=5)?4'd3:4'd0) ;assign din_add_d_temp_ff2 = din_d_temp_ff2 + ((din_d_temp_ff2>=5)?4'd3:4'd0) ;assign din_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[19:0]; endendalways @(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; endendassign din_a_temp_ff3 = din_temp_ff3[ 7: 0] ;assign din_b_temp_ff3 = din_temp_ff3[11: 8] ;assign din_c_temp_ff3 = din_temp_ff3[15:12] ;assign din_d_temp_ff3 = din_temp_ff3[19:16] ;assign din_add_a_temp_ff3 = din_a_temp_ff3 ;assign din_add_b_temp_ff3 = din_b_temp_ff3 + ((din_b_temp_ff3>=5)?4'd3:4'd0) ;assign din_add_c_temp_ff3 = din_c_temp_ff3 + ((din_c_temp_ff3>=5)?4'd3:4'd0) ;assign din_add_d_temp_ff3 = din_d_temp_ff3 + ((din_d_temp_ff3>=5)?4'd3:4'd0) ;assign din_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[19:0]; endendalways @(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; endendassign dout = din_temp_ff4[19:8];assign dout_vld = dout_vld_temp_ff4 ;endmodule
1.4 数码管显示模块设计
1.4.1接口信号
1.4.2设计思路 在前面的案例中已经有数码管显示的介绍,所以这里不在过多介绍,详细介绍请看下方链接: http://fpgabbs.com/forum.php?mod=viewthread&tid=399
1.4.3参考代码 module seg_disp( rst_n , clk , din , din_vld , seg_sel , segment );parameter DATA_IN = 12 ;parameter TIME_30MS = 15000 ;parameter SEG_WID = 8 ;parameter SEG_NUM = 8 ;parameter CNT_WID = 10 ;parameter NUM_0 = 8'b1100_0000;parameter NUM_1 = 8'b1111_1001;parameter NUM_2 = 8'b1010_0100;parameter NUM_3 = 8'b1011_0000;parameter NUM_4 = 8'b1001_1001;parameter NUM_5 = 8'b1001_0010;parameter NUM_6 = 8'b1000_0010;parameter NUM_7 = 8'b1111_1000;parameter NUM_8 = 8'b1000_0000;parameter NUM_9 = 8'b1001_0000;parameter NUM_ERR = 8'b1111_1111;input clk ;input rst_n ;input [DATA_IN - 1:0] din ;input din_vld ;output [SEG_NUM - 1:0] seg_sel ;output [SEG_WID - 1:0] segment ;reg [SEG_NUM - 1:0] seg_sel ;reg [SEG_WID - 1:0] segment ;reg [ 31 : 0] cnt_30us ;reg [SEG_NUM - 1:0] 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 ; endendassign 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; endendassign 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); endendalways @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin seg_tmp <= 0; end else begin seg_tmp <= din[4*(sel_cnt+1)-1 -:4]; endendalways@(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; default : segment <= NUM_ERR; endcase endendendmodule
1.5 效果和总结
Ø 下图是该工程在db603开发板上的现象——串口发送数据8’h93,数码管显示12’d147。
Ø 下图是该工程在mp801试验箱上的现象——串口发送数据8’he9,数码管显示12’d233。
Ø 下图是该工程在ms980试验箱上的现象——串口发送数据8’h52,数码管显示12’d082。
由于该项目的上板现象是串口发送8位的十六进制数经过BCD译码后,在数码管上显示对应的十进制数值,想观看完整现象的朋友可以看一下上板演示的视频。 本案例设计教学视频和工程源码,请到明德扬论坛学习。 感兴趣的朋友也可以访问明德扬论坛进行FPGA相关工程设计学习,也可以看一下我们往期的文章: 《基于FPGA的密码锁设计》 《波形相位频率可调DDS信号发生器》 《基于FPGA的曼彻斯特编码解码设计》 《基于FPGA的出租车计费系统》 《数电基础与Verilog设计》 《基于FPGA的频率、电压测量》 《基于FPGA的汉明码编码解码设计》 《关于锁存器问题的讨论》 《阻塞赋值与非阻塞赋值》 《参数例化时自动计算位宽的解决办法》
明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。点拨开发板——学习FPGA的入门之选。
MP801开发板——千兆网、ADDA、大容量SDRAM等,学习和项目需求一步到位。**培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA。周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO架构设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。项目承接——承接企业FPGA研发项目。人才服务——提供人才推荐、人才代培、人才派遣等服务。
|