FPGA加三移位算法:硬件逻辑实现二进制转BCD码
本文设计方式采用明德扬至简设计法。利用FPGA来完成显示功能不是个很理想的方式,当显示任务比较复杂,要通过各种算法显示波形或者特定图形时,当然要用单片机通过C语言完成这类流程控制复杂,又对时序要求不高的任务(这也坚定了我学习SOPC的决心)。但要驱动如LCD1602/LCD12864打印字符,显示系统工作状态还是比较方便的。
数字系统内部均为二进制比特信息,而打印字符需要先将其转换成BCD码,并进一步转为ASCII字符才能正常显示。这一简单算法的软件实现非常简单,但要是用硬件逻辑完成其中多个乘除法运算无疑浪费很多硬件资源,这时最常用的做法就是通过移位操作代替乘除法运算。适用于FPGA实现的二进制序列转BCD码算法是“加三移位”。小梅哥FPGA进阶系列教程中的《二进制转BCD》文章中对其进行了详细说明【小梅哥FPGA进阶教程】第二章 二进制转BCD - FPGA/CPLD - 电子工程世界-论坛http://bbs.eeworld.com.cn/thread-510929-1-1.html
本文仅重点阐述设计方式。加三移位算法以8位二进制转BCD码为例,BCD码需要3位,一共12bit(8是2的3次方)。每次将剩余的待转换二进制序列最高位左移进BCD码寄存器,每移一位后判断每一位BCD码是否大于4,若是则加3调整,否则不变。直至移位8次后结束。注:最后一次移位不需要加3调整。可以发现上述过程可以利用一个非常简单的状态机实现:

BCD码以4bit为1位,非常适合存储器模型,这里使用:reg [4-1:0] bcd_code [3-1:0];//该存储器由3个位宽为4bit的寄存器组成。每到SHIFT状态下,进行一次左移操作,随后进入ADD_3状态判断是否需要加3操作。当移位8次后进入ASCII状态利用查表法找出ASCII中对应数字,最后等待LCD控制模块完成显示任务后回到IDLE状态继续响应后续数据。以下是完整代码。
`timescale 1ns / 1ps /*
显示编码模块:
1 完成二进制数值与BCD码的转换
2 完成BCD码的字符编码
3 一次性送出拼接后编码数据
*/ module disp_code#(parameter DATA_W = )(
input clk,
input rst_n,
//MAX30102_ctrl侧接口
input [DATA_W-:] din,
input din_vld,
output reg code_rdy,
//LCD_CTRL侧接口
output reg [DATA_W-:] dout,
output reg dout_vld,
input lcd_ctrl_rdy
); /*
编码转换流程:
1 检测BCD码寄存器每四位数值是否大于4,若是则加3,否则不处理;
2 左移一位,将待转换二进制数最高位送入寄存器;
3 第n次移位后进行字符编码;
*/ localparam LOG_DATA_W = ,
STA_W = ; localparam IDLE = ;
localparam ADD_3 = ;
localparam SHIFT = ;
localparam ASCII = ;
localparam WAIT_LCD = ; reg [ (-):] shift_cnt ;
wire add_shift_cnt ;
wire end_shift_cnt ;
reg [ (-):] char_cnt ;
wire add_char_cnt ;
wire end_char_cnt ;
reg [ (DATA_W-):] data_tmp ;
reg [ (DATA_W-):] tfrac_tmp ;
reg [-:] bcd_code [LOG_DATA_W-:];
reg [ (-):] disp_data ;
wire idle2shift ;
wire add_32shift ;
wire shift2add_3 ;
wire shift2ascii ;
wire ascii2wait_lcd ;
wire wait_lcd2idle ;
reg lcd_rdy_r ;
reg busy_flag ;
reg [STA_W-:] state_c;
reg [STA_W-:] state_n;
wire lcd_rdy_pos;
wire data_in_vld; //移位次数计数器
always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
shift_cnt <= ;
end
else if(add_shift_cnt) begin
if(end_shift_cnt)
shift_cnt <= ;
else
shift_cnt <= shift_cnt+ ;
end
end
assign add_shift_cnt = (state_c == SHIFT);
assign end_shift_cnt = add_shift_cnt && shift_cnt == (DATA_W)- ; //字符个数计数器
always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
char_cnt <= ;
end
else if(add_char_cnt) begin
if(end_char_cnt)
char_cnt <= ;
else
char_cnt <= char_cnt+ ;
end
end
assign add_char_cnt = (state_c == ASCII);
assign end_char_cnt = add_char_cnt && char_cnt == (LOG_DATA_W)- ; //数据寄存
always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
data_tmp <= () ;
end
else if(data_in_vld)begin
data_tmp <= (din) ;
end
end /*********************************************状态机****************************************************/
always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
state_c <= IDLE ;
end
else begin
state_c <= state_n;
end
end always @(*) begin
case(state_c)
IDLE :begin
if(idle2shift )
state_n = SHIFT ;
else
state_n = state_c ;
end
SHIFT :begin
if(shift2add_3 )
state_n = ADD_3 ;
else if(shift2ascii )
state_n = ASCII ;
else
state_n = state_c ;
end
ADD_3 :begin
if(add_32shift )
state_n = SHIFT ;
else
state_n = state_c ;
end
ASCII :begin
if(ascii2wait_lcd )
state_n = WAIT_LCD ;
else
state_n = state_c ;
end
WAIT_LCD:begin
if(wait_lcd2idle)
state_n = IDLE;
else
state_n = state_c;
end
default : state_n = IDLE ;
endcase
end assign idle2shift = state_c == IDLE && (din_vld);
assign shift2add_3 = state_c == SHIFT && (!end_shift_cnt);
assign shift2ascii = state_c == SHIFT && (end_shift_cnt);
assign add_32shift = state_c == ADD_3 && ('b1);
assign ascii2wait_lcd = state_c == ASCII && (end_char_cnt);
assign wait_lcd2idle = state_c == WAIT_LCD && lcd_rdy_pos; /*********************************************编码过程****************************************************/
//binary code ---> 8421bcd code
always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
bcd_code[] <= () ;
end
else if(state_c == ADD_3 && bcd_code[] > 'd4)begin
bcd_code[] <= (bcd_code[] + 'd3) ;
end
else if(state_c == SHIFT)
bcd_code[] <= {bcd_code[][:],data_tmp[DATA_W--shift_cnt]};
end always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
bcd_code[] <= () ;
end
else if(state_c == ADD_3 && bcd_code[] > 'd4)begin
bcd_code[] <= (bcd_code[] + 'd3) ;
end
else if(state_c == SHIFT)
bcd_code[] <= {bcd_code[][:],bcd_code[][]};
end always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
bcd_code[] <= () ;
end
else if(state_c == ADD_3 && bcd_code[] > 'd4)begin
bcd_code[] <= (bcd_code[] + 'd3) ;
end
else if(state_c == SHIFT)
bcd_code[] <= {bcd_code[][:],bcd_code[][]};
end always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
disp_data <= () ;
end
else if(add_char_cnt)begin
disp_data <= (bcd_code[LOG_DATA_W- - char_cnt]) ;
end
end /*********************************************接口信号****************************************************/ always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
lcd_rdy_r <= () ;
end
else if(state_c == WAIT_LCD)begin
lcd_rdy_r <= (lcd_ctrl_rdy) ;
end
end assign lcd_rdy_pos = lcd_ctrl_rdy == && lcd_rdy_r == ; always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
busy_flag <= () ;
end
else if(data_in_vld)begin
busy_flag <= ('b1) ;
end
else if(wait_lcd2idle)begin
busy_flag <= () ;
end
end assign data_in_vld = state_c == IDLE && din_vld; always@(*)begin
if(!lcd_ctrl_rdy || busy_flag || data_in_vld)
code_rdy = ;
else
code_rdy = ;
end /*********************************************编码后数据输出****************************************************/
// ASCII CODE
always@(*)begin
case(disp_data)
:dout = "";
:dout = "";
:dout = "";
:dout = "";
:dout = "";
:dout = "";
:dout = "";
:dout = "";
:dout = "";
:dout = "";
default:dout = "";
endcase
end always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
dout_vld <= () ;
end
else if(add_char_cnt)begin
dout_vld <= ('b1) ;
end
else
dout_vld <= ;
end endmodule
接下来用testbench仿真验证逻辑功能,在测试向量中要模拟LCD控制模块和数据源上游模块的行为,并通过显示编码方式验证待测试模块状态机当前状态。
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2018/03/15 18:32:05
// Design Name:
// Module Name: disp_code_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
////////////////////////////////////////////////////////////////////////////////// module disp_code_tb; reg clk;
reg rst_n;
reg [-:] din;
reg din_vld;
wire code_rdy;
wire [-:] dout;
wire dout_vld;
reg lcd_ctrl_rdy; reg [ (-):] wait_cnt ;
wire add_wait_cnt ;
wire end_wait_cnt ;
reg [*-:] code_state;
reg lcd_ctrl_busy ; //待测试模块例化
disp_code#(.DATA_W())
uut(
.clk (clk),
.rst_n (rst_n), .din (din),
.din_vld (din_vld),
.code_rdy (code_rdy), .dout (dout),
.dout_vld (dout_vld),
.lcd_ctrl_rdy(lcd_ctrl_rdy)
);
parameter CYC = ,
RST_TIM = ; initial begin
clk = ;
forever #(CYC/) clk = ~clk;
end initial begin
rst_n = ;
#;
rst_n = ;
#(CYC*RST_TIM)
rst_n = ;
#100_000;
$stop;
end //模拟LCD控制模块行为
always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
lcd_ctrl_busy <= () ;
end
else if(dout_vld)begin
lcd_ctrl_busy <= ('b1) ;
end
else if(end_wait_cnt)begin
lcd_ctrl_busy <= () ;
end
end always@(*)begin
if(lcd_ctrl_busy || dout_vld)
lcd_ctrl_rdy = ;
else
lcd_ctrl_rdy = 'b1;
end always @(posedge clk or negedge rst_n) begin
if (rst_n==) begin
wait_cnt <= ;
end
else if(add_wait_cnt) begin
if(end_wait_cnt)
wait_cnt <= ;
else
wait_cnt <= wait_cnt+ ;
end
end
assign add_wait_cnt = (lcd_ctrl_rdy == );
assign end_wait_cnt = add_wait_cnt && wait_cnt == ()- ; //模拟数据源行为
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
din <= ;
else if(code_rdy)
din <= 'h20;
end always @(posedge clk or negedge rst_n )begin
if(rst_n==) begin
din_vld <= () ;
end
else if(code_rdy)begin
din_vld <= ('b1) ;
end
else begin
din_vld <= () ;
end
end //状态显示编码
always@(*)begin
case(uut.state_c)
'd0:code_state = "IDLE";
'd1:code_state = "ADD_3";
'd2:code_state = "SHIFT";
'd3:code_state = "ASCII";
'd4:code_state = "WAIT_LCD";
default:code_state = "ERROR";
endcase
end endmodule
分别看看显示编码模块仿真波形的整体和局部放大图:


可以看出在LCD控制模块准备好情况下(lcd_ctrl_rdy拉高),显示编码模块也处于准备就绪状态,上游模块送入待转码数据8'h20,对应的十进制数是32,显示编码模块输出结果与数值相符合。
FPGA加三移位算法:硬件逻辑实现二进制转BCD码的更多相关文章
- FPGA中将十进制数在数码管中显示(verilog版)--二进制转换为BCD码
这周有朋友问怎样在fpga中用数码管来显示一个十进制数,比如1000.每个数码管上显示一位十进制数.如果用高级语言来分离各位,只需要分别对该数做1000,100,10对应的取商和取余即可分离出千百十个 ...
- 基于FPGA的腐蚀膨胀算法实现
本篇文章我要写的是基于的腐蚀膨胀算法实现,腐蚀膨胀是形态学图像处理的基础,,腐蚀在二值图像的基础上做"收缩"或"细化"操作,膨胀在二值图像的基础上做" ...
- 基于FPGA的肤色识别算法实现
大家好,给大家介绍一下,这是基于FPGA的肤色识别算法实现. 我们今天这篇文章有两个内容一是实现基于FPGA的彩色图片转灰度实现,然后在这个基础上实现基于FPGA的肤色检测算法实现. 将彩色图像转化为 ...
- 目标反射回波检测算法及其FPGA实现 之一:算法概述
目标反射回波检测算法及其FPGA实现之一:算法概述 前段时间,接触了一个声呐目标反射回波检测的项目.声呐接收机要实现的核心功能是在含有大量噪声的反射回波中,识别出发射机发出的激励信号的回波.我会分几篇 ...
- Java利用DES/3DES/AES这三种算法分别实现对称加密
转载地址:http://blog.csdn.net/smartbetter/article/details/54017759 有两句话是这么说的: 1)算法和数据结构就是编程的一个重要部分,你若失掉了 ...
- 二进制 转换成十进制 BCD码(加3移位法)
"原来的二进制数十几位,则左移时就要左移几位" "二进制数调整BCD码的方法是将二进制码左移8次,每次移位后都检查低四位LSD+3是否大于7,如是则加3,否则不加,高4位 ...
- 分类算法之逻辑回归(Logistic Regression
分类算法之逻辑回归(Logistic Regression) 1.二分类问题 现在有一家医院,想要对病人的病情进行分析,其中有一项就是关于良性\恶性肿瘤的判断,现在有一批数据集是关于肿瘤大小的,任务就 ...
- 分布式共识算法 (三) Raft算法
系列目录 分布式共识算法 (一) 背景 分布式共识算法 (二) Paxos算法 分布式共识算法 (三) Raft算法 分布式共识算法 (四) BTF算法 一.引子 1.1 介绍 Raft 是一种为了管 ...
- SparkMLlib学习分类算法之逻辑回归算法
SparkMLlib学习分类算法之逻辑回归算法 (一),逻辑回归算法的概念(参考网址:http://blog.csdn.net/sinat_33761963/article/details/51693 ...
随机推荐
- js到底new了点啥
在最开始学习js的时候,我看书上写着,创建一个数组,一个对象通常使用new,如下: var arr=new Array(),//arr=[] obj=new Object();//obj={} 到了后 ...
- 你所有不知的margin属性
前言 致谢 本文总结于 张鑫旭老师的 CSS深入理解之margin课程,感谢张老师的辛苦付出! 难学的 CSS 作为前端狗的我们,每天都要和网页打交道.当 UI 将设计稿发给你时,CSS 的知识便显得 ...
- win10外接键盘失灵
故障描述:笔记本外接的键盘突然之间就失灵,键盘的灯不亮,无法输入 处理方程: 1. 我的电脑右击--> 管理 --> 设备管理器(开始失灵时,键盘下的HID Keyboard Device ...
- HttpURLConnection发送请求
每个 HttpURLConnection 实例都可用于生成单个请求,但是其他实例可以透明地共享连接到 HTTP 服务器的基础网络.请求后在 HttpURLConnection 的 InputStrea ...
- Java AES加密案例
AES加密原理 http://www.blogjava.net/amigoxie/archive/2014/07/06/415503.html PHP 加密 https://segmentfault. ...
- js将汉字转为相应的拼音
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8&quo ...
- Hi3531 SDK 安装以及升级使用说明
Hi3531 SDK 安装以及升级使用说明 第一章 Hi3531_SDK_Vx.x.x.x版本升级操作说明 如果您是首次安装本SDK,请直接参看第2章. 第二章 首次安装SDK 1.Hi ...
- VME总线
简介 诞生于25年前的VME(VersaModule Eurocard)总线是一种通用的计算机总线,结合了Motorola公司Versa总线的电气标准和在欧洲建立的Eurocard标准的机械形状因子, ...
- VxWorks6.6 pcPentium BSP 使用说明(三):设备驱动
本文主要介绍了pcPentium BSP中包含的驱动程序.包含了官方提供的所有驱动程序,除了aic7888Lib--现在已用得很少的一个AIC-7888 SCSI控制器的驱动介绍.建议重点阅读at ...
- Servlet.service() for Servlet jsp threw exception javax.servlet.ServletException:File "/pageFoo
1.错误描述 Servlet.service() for Servlet jsp threw exception javax.servlet.ServletException:File "/ ...