• 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状态继续响应后续数据。以下是完整代码。

      1 `timescale 1ns / 1ps
      2 
      3 /*
      4 显示编码模块:
      5     1 完成二进制数值与BCD码的转换
      6     2 完成BCD码的字符编码
      7     3 一次性送出拼接后编码数据
      8 */
      9 
     10 module disp_code#(parameter DATA_W = 8)(
     11     input                       clk,
     12     input                       rst_n,
     13     //MAX30102_ctrl侧接口
     14     input       [DATA_W-1:0]    din,
     15     input                       din_vld,
     16     output reg                  code_rdy,
     17     //LCD_CTRL侧接口
     18     output reg [DATA_W-1:0]     dout,
     19     output reg                  dout_vld,
     20     input                       lcd_ctrl_rdy
     21 );
     22 
     23 /*
     24 编码转换流程:
     25 1 检测BCD码寄存器每四位数值是否大于4,若是则加3,否则不处理;
     26 2 左移一位,将待转换二进制数最高位送入寄存器;
     27 3 第n次移位后进行字符编码;
     28 */
     29 
     30 localparam LOG_DATA_W = 3,
     31            STA_W      = 5;
     32 
     33 localparam  IDLE        = 0 ;
     34 localparam  ADD_3       = 1 ;
     35 localparam  SHIFT       = 2 ;
     36 localparam  ASCII       = 3 ;
     37 localparam  WAIT_LCD    = 4 ;
     38 
     39 
     40 reg [ (8-1):0]      shift_cnt     ;
     41 wire                add_shift_cnt ;
     42 wire                end_shift_cnt ;
     43 reg [ (4-1):0]      char_cnt     ;
     44 wire                add_char_cnt ;
     45 wire                end_char_cnt ;
     46 reg [ (DATA_W-1):0] data_tmp     ;
     47 reg [ (DATA_W-1):0] tfrac_tmp     ;
     48 reg [4-1:0]         bcd_code [LOG_DATA_W-1:0];
     49 reg [ (4-1):0]      disp_data     ;
     50 wire                idle2shift     ;
     51 wire                add_32shift    ;
     52 wire                shift2add_3    ;
     53 wire                shift2ascii    ;
     54 wire                ascii2wait_lcd ;
     55 wire                wait_lcd2idle  ;
     56 reg                 lcd_rdy_r     ;
     57 reg                 busy_flag     ;
     58 reg [STA_W-1:0]     state_c;
     59 reg [STA_W-1:0]     state_n;
     60 wire                lcd_rdy_pos;
     61 wire                data_in_vld;
     62 
     63 //移位次数计数器
     64 always @(posedge clk or negedge rst_n) begin 
     65     if (rst_n==0) begin
     66         shift_cnt <= 0; 
     67     end
     68     else if(add_shift_cnt) begin
     69         if(end_shift_cnt)
     70             shift_cnt <= 0; 
     71         else
     72             shift_cnt <= shift_cnt+1 ;
     73    end
     74 end
     75 assign add_shift_cnt = (state_c == SHIFT);
     76 assign end_shift_cnt = add_shift_cnt  && shift_cnt == (DATA_W)-1 ;
     77 
     78 //字符个数计数器
     79 always @(posedge clk or negedge rst_n) begin 
     80     if (rst_n==0) begin
     81         char_cnt <= 0; 
     82     end
     83     else if(add_char_cnt) begin
     84         if(end_char_cnt)
     85             char_cnt <= 0; 
     86         else
     87             char_cnt <= char_cnt+1 ;
     88    end
     89 end
     90 assign add_char_cnt = (state_c == ASCII);
     91 assign end_char_cnt = add_char_cnt  && char_cnt == (LOG_DATA_W)-1 ;
     92 
     93 //数据寄存
     94 always @(posedge clk or negedge rst_n )begin 
     95     if(rst_n==0) begin
     96         data_tmp <= (0)  ;
     97     end
     98     else if(data_in_vld)begin
     99         data_tmp <= (din)  ;
    100     end 
    101 end
    102 
    103 
    104 /*********************************************状态机****************************************************/
    105 always @(posedge clk or negedge rst_n) begin 
    106     if (rst_n==0) begin
    107         state_c <= IDLE ;
    108     end
    109     else begin
    110         state_c <= state_n;
    111    end
    112 end
    113 
    114 always @(*) begin 
    115     case(state_c)  
    116         IDLE :begin
    117             if(idle2shift ) 
    118                 state_n = SHIFT ;
    119             else 
    120                 state_n = state_c ;
    121         end
    122         SHIFT :begin
    123             if(shift2add_3 ) 
    124                 state_n = ADD_3 ;
    125             else if(shift2ascii ) 
    126                 state_n = ASCII ;
    127             else 
    128                 state_n = state_c ;
    129         end
    130         ADD_3 :begin
    131             if(add_32shift ) 
    132                 state_n = SHIFT ;
    133             else 
    134                 state_n = state_c ;
    135         end
    136         ASCII :begin
    137             if(ascii2wait_lcd ) 
    138                 state_n = WAIT_LCD ;
    139             else 
    140                 state_n = state_c ;
    141         end
    142         WAIT_LCD:begin
    143             if(wait_lcd2idle)
    144                 state_n = IDLE;
    145             else
    146                 state_n = state_c;
    147         end
    148         default : state_n = IDLE ;
    149     endcase
    150 end
    151 
    152 assign idle2shift       = state_c == IDLE       && (din_vld);
    153 assign shift2add_3      = state_c == SHIFT      && (!end_shift_cnt);
    154 assign shift2ascii      = state_c == SHIFT      && (end_shift_cnt);
    155 assign add_32shift      = state_c == ADD_3      && (1'b1);
    156 assign ascii2wait_lcd   = state_c == ASCII      && (end_char_cnt);
    157 assign wait_lcd2idle    = state_c == WAIT_LCD   && lcd_rdy_pos;
    158 
    159 /*********************************************编码过程****************************************************/
    160 //binary code ---> 8421bcd code
    161 always @(posedge clk or negedge rst_n )begin 
    162     if(rst_n==0) begin
    163         bcd_code[0] <= (0)  ;
    164     end
    165     else if(state_c == ADD_3 && bcd_code[0] > 4'd4)begin
    166         bcd_code[0] <= (bcd_code[0] + 4'd3)  ;
    167     end 
    168     else if(state_c == SHIFT)
    169         bcd_code[0] <= {bcd_code[0][2:0],data_tmp[DATA_W-1-shift_cnt]};
    170 end
    171 
    172 always @(posedge clk or negedge rst_n )begin 
    173     if(rst_n==0) begin
    174         bcd_code[1] <= (0)  ;
    175     end
    176     else if(state_c == ADD_3 && bcd_code[1] > 4'd4)begin
    177         bcd_code[1] <= (bcd_code[1] + 4'd3)  ;
    178     end 
    179     else if(state_c == SHIFT)
    180         bcd_code[1] <= {bcd_code[1][2:0],bcd_code[0][3]};
    181 end
    182 
    183 always @(posedge clk or negedge rst_n )begin 
    184     if(rst_n==0) begin
    185         bcd_code[2] <= (0)  ;
    186     end
    187     else if(state_c == ADD_3 && bcd_code[2] > 4'd4)begin
    188         bcd_code[2] <= (bcd_code[2] + 4'd3)  ;
    189     end 
    190     else if(state_c == SHIFT)
    191         bcd_code[2] <= {bcd_code[2][2:0],bcd_code[1][3]};
    192 end
    193 
    194 always @(posedge clk or negedge rst_n )begin 
    195     if(rst_n==0) begin
    196         disp_data <= (0)  ;
    197     end
    198     else if(add_char_cnt)begin
    199         disp_data <= (bcd_code[LOG_DATA_W-1  - char_cnt])  ;
    200     end 
    201 end
    202 
    203 /*********************************************接口信号****************************************************/
    204 
    205 always @(posedge clk or negedge rst_n )begin 
    206     if(rst_n==0) begin
    207         lcd_rdy_r <= (0)  ;
    208     end
    209     else if(state_c == WAIT_LCD)begin
    210         lcd_rdy_r <= (lcd_ctrl_rdy)  ;
    211     end 
    212 end
    213 
    214 assign lcd_rdy_pos = lcd_ctrl_rdy == 1 && lcd_rdy_r == 0;
    215 
    216 always @(posedge clk or negedge rst_n )begin 
    217     if(rst_n==0) begin
    218         busy_flag <= (0)  ;
    219     end
    220     else if(data_in_vld)begin
    221         busy_flag <= (1'b1)  ;
    222     end 
    223     else if(wait_lcd2idle)begin
    224         busy_flag <= (0)  ;
    225     end 
    226 end
    227 
    228 assign data_in_vld = state_c == IDLE && din_vld;
    229 
    230 always@(*)begin
    231     if(!lcd_ctrl_rdy || busy_flag || data_in_vld)
    232         code_rdy = 0;
    233     else
    234         code_rdy = 1;
    235 end
    236 
    237 
    238 /*********************************************编码后数据输出****************************************************/
    239 // ASCII CODE
    240 always@(*)begin
    241     case(disp_data)
    242         0:dout = "0";
    243         1:dout = "1";
    244         2:dout = "2";
    245         3:dout = "3";
    246         4:dout = "4";
    247         5:dout = "5";
    248         6:dout = "6";
    249         7:dout = "7";
    250         8:dout = "8";
    251         9:dout = "9";
    252         default:dout = "0";
    253     endcase
    254 end
    255 
    256 always @(posedge clk or negedge rst_n )begin 
    257     if(rst_n==0) begin
    258         dout_vld <= (0)  ;
    259     end
    260     else if(add_char_cnt)begin
    261         dout_vld <= (1'b1)  ;
    262     end 
    263     else
    264         dout_vld <= 0;
    265 end
    266 
    267 endmodule 

       接下来用testbench仿真验证逻辑功能,在测试向量中要模拟LCD控制模块和数据源上游模块的行为,并通过显示编码方式验证待测试模块状态机当前状态。

      1 `timescale 1ns / 1ps
      2 //////////////////////////////////////////////////////////////////////////////////
      3 // Company: 
      4 // Engineer: 
      5 // 
      6 // Create Date: 2018/03/15 18:32:05
      7 // Design Name: 
      8 // Module Name: disp_code_tb
      9 // Project Name: 
     10 // Target Devices: 
     11 // Tool Versions: 
     12 // Description: 
     13 // 
     14 // Dependencies: 
     15 // 
     16 // Revision:
     17 // Revision 0.01 - File Created
     18 // Additional Comments:
     19 // 
     20 //////////////////////////////////////////////////////////////////////////////////
     21 
     22 
     23 module disp_code_tb;
     24 
     25 reg clk;
     26 reg rst_n;
     27 reg [8-1:0] din;
     28 reg din_vld;
     29 wire code_rdy;
     30 wire [8-1:0] dout;
     31 wire dout_vld;
     32 reg lcd_ctrl_rdy;
     33 
     34 reg [ (4-1):0]  wait_cnt ;
     35 wire        add_wait_cnt ;
     36 wire        end_wait_cnt ;
     37 reg [8*8-1:0] code_state;
     38 reg   lcd_ctrl_busy     ;
     39 
     40 //待测试模块例化
     41  disp_code#(.DATA_W(8))
     42  uut(
     43      .clk         (clk),
     44      .rst_n       (rst_n),
     45     
     46      .din         (din),
     47      .din_vld     (din_vld),
     48      .code_rdy    (code_rdy),
     49     
     50      .dout        (dout),
     51      .dout_vld    (dout_vld),
     52      .lcd_ctrl_rdy(lcd_ctrl_rdy)
     53 );
     54 parameter CYC = 20,
     55           RST_TIM = 2;
     56 
     57 initial begin
     58     clk = 1;
     59     forever #(CYC/2) clk = ~clk;
     60 end
     61 
     62 initial begin
     63     rst_n = 1;
     64     #1;
     65     rst_n = 0;
     66     #(CYC*RST_TIM) 
     67     rst_n = 1;
     68     #100_000;
     69     $stop;
     70 end
     71 
     72 //模拟LCD控制模块行为
     73 always @(posedge clk or negedge rst_n )begin 
     74     if(rst_n==0) begin
     75         lcd_ctrl_busy <= (0)  ;
     76     end
     77     else if(dout_vld)begin
     78         lcd_ctrl_busy <= (1'b1)  ;
     79     end 
     80     else if(end_wait_cnt)begin
     81         lcd_ctrl_busy <= (0)  ;
     82     end 
     83 end
     84 
     85 always@(*)begin
     86     if(lcd_ctrl_busy || dout_vld)
     87         lcd_ctrl_rdy  = 0;
     88     else
     89         lcd_ctrl_rdy = 1'b1;
     90 end
     91 
     92 always @(posedge clk or negedge rst_n) begin 
     93     if (rst_n==0) begin
     94         wait_cnt <= 0; 
     95     end
     96     else if(add_wait_cnt) begin
     97         if(end_wait_cnt)
     98             wait_cnt <= 0; 
     99         else
    100             wait_cnt <= wait_cnt+1 ;
    101    end
    102 end
    103 assign add_wait_cnt = (lcd_ctrl_rdy == 0);
    104 assign end_wait_cnt = add_wait_cnt  && wait_cnt == (10)-1 ;
    105 
    106 //模拟数据源行为
    107 always@(posedge clk or negedge rst_n)begin
    108     if(!rst_n)
    109         din <= 0;
    110     else if(code_rdy)
    111         din <= 8'h20;
    112 end
    113 
    114 always @(posedge clk or negedge rst_n )begin 
    115     if(rst_n==0) begin
    116         din_vld <= (0)  ;
    117     end
    118     else if(code_rdy)begin
    119         din_vld <= (1'b1)  ;
    120     end 
    121     else begin
    122         din_vld <= (0)  ;
    123     end 
    124 end
    125 
    126 //状态显示编码
    127 always@(*)begin
    128     case(uut.state_c)
    129         5'd0:code_state = "IDLE";
    130         5'd1:code_state = "ADD_3";
    131         5'd2:code_state = "SHIFT";
    132         5'd3:code_state = "ASCII";
    133         5'd4:code_state = "WAIT_LCD";
    134         default:code_state = "ERROR";
    135     endcase
    136 end
    137 
    138 endmodule

       分别看看显示编码模块仿真波形的整体和局部放大图:

      可以看出在LCD控制模块准备好情况下(lcd_ctrl_rdy拉高),显示编码模块也处于准备就绪状态,上游模块送入待转码数据8'h20,对应的十进制数是32,显示编码模块输出结果与数值相符合。

  • 相关阅读:
    tomcat 登录主页成功 点击Manager App 401 等问题
    servlet
    jsp 记录1 bs/cs
    java jar
    Java 第四课 对象 类
    java 第五课 异常
    Java 第三课 数组排序
    java 第二课 标识符
    java 第一课 笔记
    maven 项目问题集锦
  • 原文地址:https://www.cnblogs.com/moluoqishi/p/8575302.html
Copyright © 2020-2023  润新知