本文设计方式采用明德扬至简设计法。利用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码的更多相关文章

  1. FPGA中将十进制数在数码管中显示(verilog版)--二进制转换为BCD码

    这周有朋友问怎样在fpga中用数码管来显示一个十进制数,比如1000.每个数码管上显示一位十进制数.如果用高级语言来分离各位,只需要分别对该数做1000,100,10对应的取商和取余即可分离出千百十个 ...

  2. 基于FPGA的腐蚀膨胀算法实现

    本篇文章我要写的是基于的腐蚀膨胀算法实现,腐蚀膨胀是形态学图像处理的基础,,腐蚀在二值图像的基础上做"收缩"或"细化"操作,膨胀在二值图像的基础上做" ...

  3. 基于FPGA的肤色识别算法实现

    大家好,给大家介绍一下,这是基于FPGA的肤色识别算法实现. 我们今天这篇文章有两个内容一是实现基于FPGA的彩色图片转灰度实现,然后在这个基础上实现基于FPGA的肤色检测算法实现. 将彩色图像转化为 ...

  4. 目标反射回波检测算法及其FPGA实现 之一:算法概述

    目标反射回波检测算法及其FPGA实现之一:算法概述 前段时间,接触了一个声呐目标反射回波检测的项目.声呐接收机要实现的核心功能是在含有大量噪声的反射回波中,识别出发射机发出的激励信号的回波.我会分几篇 ...

  5. Java利用DES/3DES/AES这三种算法分别实现对称加密

    转载地址:http://blog.csdn.net/smartbetter/article/details/54017759 有两句话是这么说的: 1)算法和数据结构就是编程的一个重要部分,你若失掉了 ...

  6. 二进制 转换成十进制 BCD码(加3移位法)

    "原来的二进制数十几位,则左移时就要左移几位" "二进制数调整BCD码的方法是将二进制码左移8次,每次移位后都检查低四位LSD+3是否大于7,如是则加3,否则不加,高4位 ...

  7. 分类算法之逻辑回归(Logistic Regression

    分类算法之逻辑回归(Logistic Regression) 1.二分类问题 现在有一家医院,想要对病人的病情进行分析,其中有一项就是关于良性\恶性肿瘤的判断,现在有一批数据集是关于肿瘤大小的,任务就 ...

  8. 分布式共识算法 (三) Raft算法

    系列目录 分布式共识算法 (一) 背景 分布式共识算法 (二) Paxos算法 分布式共识算法 (三) Raft算法 分布式共识算法 (四) BTF算法 一.引子 1.1 介绍 Raft 是一种为了管 ...

  9. SparkMLlib学习分类算法之逻辑回归算法

    SparkMLlib学习分类算法之逻辑回归算法 (一),逻辑回归算法的概念(参考网址:http://blog.csdn.net/sinat_33761963/article/details/51693 ...

随机推荐

  1. 【基础】26个命令玩转linux,菜鸟及面试必备

    1 查看目录与文件:ls #显示当前目录下所有文件的详细信息 ls -la 2 切换目录:cd #切换当前目录为/opt/test cd /opt/test 3 显示当前目录:pwd pwd 4 创建 ...

  2. C/C++语言简介之关键字

    关键字又称为保留字,就是已被C语言本身使用,不能作其它用途使用的字.例如关键字不能用作变量名.函数名等标识符.由ISO标准定义的C语言关键字共32个:auto.double.int.struct.br ...

  3. PHP实现水印效果(文字、图片)

    第一种 <?php /** * 功能:给一张图片加上水印效果 * $i 要加水印效果的图片 * $t 水印文字 * $size 文字大小 * $pos 水印的位置 * $color 文字的颜色 ...

  4. 1.9 list 列表

    列表是什么? list是Python中的基本数据结构之一,属于可变序列,所以前文中讲的可变序列的通用操作都适用于list. 这一节讲列表的特性吧. 特性一: 列表是包含任意对象的有序集合,同一个列表中 ...

  5. nyoj 1022 合纵连横 经典并查集

    思路:关键在于并查集的删点操作. 给每个诸侯国一个另外的编号,比如box[i]表示诸侯国i现在处于第box[i]个联盟,可以随时改变它的联盟编号,并且让box[i] = k, 实现删除操作.以前联盟中 ...

  6. hdu1496 打表

    通常可以想到直接四个for枚举,但是会超时.就算只用三个for也很危险.可以用打表的方法将时间复杂度降到O(n^2),注意到x1,x2,x3,x4的取值区间是关于零对称的,因此可以只考虑正整数部分,洗 ...

  7. hdu2089 不要62--经典数位DP

    一道十分经典的数位DP的题目. dp[i][j]表示最高位是数字i,连同最高位在内共有j位.注意边界的初始化. 接下来就是区间划分,特殊情况处理.....对了,如果不知道自己的方法是否正确,可以写一个 ...

  8. HDU - 3567 IDA* + 曼哈顿距离 + 康托 [kuangbin带你飞]专题二

    这题难度颇大啊,TLE一天了,测试数据组数太多了.双向广度优先搜索不能得到字典序最小的,一直WA. 思路:利用IDA*算法,当前状态到达目标状态的可能最小步数就是曼哈顿距离,用于搜索中的剪枝.下次搜索 ...

  9. 小程序中曾经遇到的坑(1)----canvas画布

    目前正在开发小程序,在开发过程中,总会遇到一些坑,而这些坑并不会有很多开发者遇到而说出来.这里先记录一条我开发过程中遇到的问题,以便后人在开发中能够更容易的解决问题!!! 首先,小程序在canvas画 ...

  10. Docker系统八:Docker的存储驱动

    Docker存储驱动 1. Docker存储驱动历史 Docker目前支持的greph driver包括: AUFS device-mapper btrfs overlayfs(重点) 关于各存储驱的 ...