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 ...
随机推荐
- 用node.js搭建本地服务器
我的第一篇笔记来写写node.js,我对node.js的并不是很了解,基本的项目路径变换还是会的.原先我下载node.js就是我想学vue.js,后来因为工作的繁忙搁浅了我的计划.最近在学习phase ...
- 项目中AppDelegate详解
1.AppDelegate.h //模板默认引入程序需要使用“类”的框架,即UIKit.h头文件,使它包含在程序中 #import <UIKit/UIKit.h> //此处@class声明 ...
- hihoCoder 1051 : 补提交卡 枚举
思路:预处理cnt(i)表示前i个数中有多少天需要补提交卡,枚举各个连续区间,区间[j, i]中需要补提交卡的天数是cnt(i) - cnt(j-1),判断m是否大于等于cnt(i) - cnt(j- ...
- hdu 1010 回溯加奇偶性剪枝
普通的剪枝会超时,必须加入奇偶性剪枝. 直接上图: AC代码: #include<cstdio> #include<cstring> #include<algorithm ...
- 使用org.apache.commons.logging打日志注意事项
使用方法:例如,protected final Log logger = LogFactory.getLog(getClass());if (logger.isDebugEnabled()) { lo ...
- 在SpringBoot使用Druid进行数据监控
前言 之前在构建项目初始设计的时候在选择数据库连接的时候就看到Druid有这样的强大的功能.数据监控.对于一个项目来说,数据监控特别重要,之前使用对于数据库的监控都是通过mysql的日志等系统来完成的 ...
- 记录 serverSocket socket 输入,输出流,关闭顺序,阻塞,PrintWriter的一些问题.
关于socket.getOutputStream() 的一些问题, OutputStream的flush是一个空方法,所以需要另一个实现了Flush的流来包装一下 这里为什么使用PrintWriter ...
- Linux sed 和 awk的用法
sed用法: 原文链接:http://www.cnblogs.com/dong008259/archive/2011/12/07/2279897.html sed是一个很好的文件处理工具,本身是一个管 ...
- Java中equal和==区别及String创建过程
Java中equal和==区别 1.起因 在一段Java代码中,使用了两种实现方式. //第一种命令行输入 int main (String[] args) { if(args[0] == " ...
- TCP协议—三次握手四次挥手的原理<转>
三次握手四次挥手的原理 TCP是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接.在TCP/IP协议中,TCP 协议提供可靠的连接服务,连接是通过三次握手进行初始化的.三 ...