在项目设计中,经常需要显示一些数值,比如温湿度,时间等等。在数字电路中数据都是用二进制的形式存储,要想显示就需要进行转换,对于一个两位的数值,对10取除可以得到其十位的数值,对10取余可以得到个位的数值。对于Verilog来说它的标准是支持除法和取余运算的,综合器也会有IP可以进行除法运算。但是这样未免会耗费太多资源,使用移位加3算法就可以实现二进制到BCD码之间的转换。
BCD码(Binary-Coded Decimal)亦称二进码十进数或二-十进制代码。用4位二进制数来表示1位十进制数中的0~9这10个数码。
移位加3算法简单来说就是,有多少位二进制说,就进行多少次移位,以八位的二进制为例,其数值最高可为三位十进制数,进行如下表左移,在移位的过程中,如果移位出的数值大于4,则将改为的数值加3后再进行移位。
这里为什么大于四,BCD码是四位二进制数表示一个十进制数的一位,如果这以为大于4,比如5,4’b0101,下一次移位后变成了4’b1010,BCD码中是没有4’b1010的,所以要加6,向高位进位。这里就是移位后加6和移位前加3,两种方法修正,我这里选择了移位前加3。(4’b0011左移后也是4’b0110,移位前和移位后都是一样的对BCD码的位数进行修正)。
为什么用左移的方法呢?这是因为二进制数和十进制数之间的位权的关系。所以二进数和十进制数之间的转化是乘以2,也就是左移一位。转换公式大概就是这个样子。
公式编辑采用Markdown编辑器Typora完成,Typora支持LaTex语法,编写公式真是爽。
代码实现起来不是很复杂,博主在网上搜索到有些代码使用纯组合逻辑实现的,用了一个for循环,我个人认为这种写法不是很好,所以自己用状态机写了一个。模块设计如下,tran_en是转换使能信号,可以使电平使能也可以是脉冲使能,作为脉冲使能使用的时候,需要在数据来临之后的一个时钟周期给出使能(我的模块是这样的特点),电平使能有效时,需要18个时钟周期完成转换,输入二进制位16bit,输出为四组千百十个位。转换完成后输出一个转换完成标志tran_done。
内部的时序我采用了三段式状态机来完成。IDLE状态接收到使能信号,调到移位状态,经过16次移位。在shift_cnt为17时(这里是因为我状态机的原理所以shift_cnt会计数到17,但移位次数为16),数据转换完成。跳到DONE状态,输出转换完成标志。
采用组合逻辑来实现,移位后的数据值的判断,大于4加3后再进行移位。最后将转换完成后的结果输出。
代码如下:
1 `timescale 1ns/1ps
2 // *********************************************************************************
3 // Project Name :
4 // Author : NingHeChuan
5 // Email : ninghechuan@foxmail.com
6 // Blogs : http://www.cnblogs.com/ninghechuan/
7 // File Name : Bin_BCD.v
8 // Module Name :
9 // Called By :
10 // Abstract :
11 //
12 // CopyRight(c) 2018, NingHeChuan Studio..
13 // All Rights Reserved
14 //
15 // *********************************************************************************
16 // Modification History:
17 // Date By Version Change Description
18 // -----------------------------------------------------------------------
19 // 2018/8/12 NingHeChuan 1.0 Original
20 //
21 // *********************************************************************************
22
23 module Bin_BCD
24 #(
25 parameter DATA_WIDTH = 16,
26 parameter SHIFT_WIDTH = 5,
27 parameter SHIFT_DEPTH = 16
28
29 )
30 (
31 input clk,
32 input rst_n,
33 input tran_en,
34 input [DATA_WIDTH - 1:0] data_in,
35 output reg tran_done,
36 output [3:0] thou_data, //千位
37 output [3:0] hund_data, //百位
38 output [3:0] tens_data, //十位
39 output [3:0] unit_data //个位
40
41 );
42 //-------------------------------------------------------
43 localparam IDLE = 3'b001;
44 localparam SHIFT = 3'b010;
45 localparam DONE = 3'b100;
46
47 //-------------------------------------------------------
48 reg [2:0] pre_state;
49 reg [2:0] next_state;
50 //
51 reg [SHIFT_DEPTH-1:0] shift_cnt;
52 //
53 reg [DATA_WIDTH:0] data_reg;
54 reg [3:0] thou_reg;
55 reg [3:0] hund_reg;
56 reg [3:0] tens_reg;
57 reg [3:0] unit_reg;
58 reg [3:0] thou_out;
59 reg [3:0] hund_out;
60 reg [3:0] tens_out;
61 reg [3:0] unit_out;
62 wire [3:0] thou_tmp;
63 wire [3:0] hund_tmp;
64 wire [3:0] tens_tmp;
65 wire [3:0] unit_tmp;
66
67 //-------------------------------------------------------
68 //FSM step1
69 always @(posedge clk or negedge rst_n)begin
70 if(rst_n == 1'b0)begin
71 pre_state <= IDLE;
72 end
73 else begin
74 pre_state <= next_state;
75 end
76 end
77
78 //FSM step2
79 always @(*)begin
80 case(pre_state)
81 IDLE:begin
82 if(tran_en == 1'b1)
83 next_state = SHIFT;
84 else
85 next_state = IDLE;
86 end
87 SHIFT:begin
88 if(shift_cnt == SHIFT_DEPTH + 1)
89 next_state = DONE;
90 else
91 next_state = SHIFT;
92 end
93 DONE:begin
94 next_state = IDLE;
95 end
96 default:next_state = IDLE;
97 endcase
98 end
99
100 //FSM step3
101 always @(posedge clk or negedge rst_n)begin
102 if(rst_n == 1'b0)begin
103 thou_reg <= 4'b0;
104 hund_reg <= 4'b0;
105 tens_reg <= 4'b0;
106 unit_reg <= 4'b0;
107 tran_done <= 1'b0;
108 shift_cnt <= 'd0;
109 data_reg <= 'd0;
110 end
111 else begin
112 case(next_state)
113 IDLE:begin
114 thou_reg <= 4'b0;
115 hund_reg <= 4'b0;
116 tens_reg <= 4'b0;
117 unit_reg <= 4'b0;
118 tran_done <= 1'b0;
119 shift_cnt <= 'd0;
120 data_reg <= data_in;
121 end
122 SHIFT:begin
123 if(shift_cnt == SHIFT_DEPTH + 1)
124 shift_cnt <= 'd0;
125 else begin
126 shift_cnt <= shift_cnt + 1'b1;
127 data_reg <= data_reg << 1;
128 unit_reg <= {unit_tmp[2:0], data_reg[16]};
129 tens_reg <= {tens_tmp[2:0], unit_tmp[3]};
130 hund_reg <= {hund_tmp[2:0], tens_tmp[3]};
131 thou_reg <= {thou_tmp[2:0], hund_tmp[3]};
132 end
133 end
134 DONE:begin
135 tran_done <= 1'b1;
136 end
137 default:begin
138 thou_reg <= thou_reg;
139 hund_reg <= hund_reg;
140 tens_reg <= tens_reg;
141 unit_reg <= unit_reg;
142 tran_done <= tran_done;
143 shift_cnt <= shift_cnt;
144 end
145 endcase
146 end
147 end
148 //-------------------------------------------------------
149 always @(posedge clk or negedge rst_n)begin
150 if(rst_n == 1'b0)begin
151 thou_out <= 'd0;
152 hund_out <= 'd0;
153 tens_out <= 'd0;
154 unit_out <= 'd0;
155 end
156 else if(tran_done == 1'b1)begin
157 thou_out <= thou_reg;
158 hund_out <= hund_reg;
159 tens_out <= tens_reg;
160 unit_out <= unit_reg;
161 end
162 else begin
163 thou_out <= thou_out;
164 hund_out <= hund_out;
165 tens_out <= tens_out;
166 unit_out <= unit_out;
167 end
168 end
169
170
171 //-------------------------------------------------------
172 assign thou_tmp = (thou_reg > 4'd4)? (thou_reg + 2'd3) : thou_reg;
173 assign hund_tmp = (hund_reg > 4'd4)? (hund_reg + 2'd3) : hund_reg;
174 assign tens_tmp = (tens_reg > 4'd4)? (tens_reg + 2'd3) : tens_reg;
175 assign unit_tmp = (unit_reg > 4'd4)? (unit_reg + 2'd3) : unit_reg;
176
177 assign thou_data = thou_out;
178 assign hund_data = hund_out;
179 assign tens_data = tens_out;
180 assign unit_data = unit_out;
181
182
183 endmodule
转载请注明出处:NingHeChuan(宁河川)
个人微信订阅号:开源FPGA
如果你想及时收到个人撰写的博文推送,可以扫描左边二维码(或者长按识别二维码)关注个人微信订阅号
知乎ID:NingHeChuan
微博ID:NingHeChuan
原文地址:https://www.cnblogs.com/ninghechuan/p/9464037.html
来源:oschina
链接:https://my.oschina.net/u/4412037/blog/4404555