Friday, 2 November 2018

fpga - VHDL - Convert from binary/integer to BCD and display it on the 7-segment display


As part of my project for the Digital System Design course I have to use a component to display on the 7-segment display a INTEGER range 0 to 9999 (or a std_logic_vector(13 downto 0)). I used the double dabble algorithm to convert a 14 bit binary number into BCD and then extracted the 4 digits. I tested the component in Active-HDL and it works good, but once loaded on my Basys2 FPGA board, it displays the wrong numbers. For example, instead of "1770", I get "0064".



I am pretty sure that the problem is caused by something from the first process, where the BCD digits are generated, but I couldn't figure out what the problem is. Here is the code of the component:


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity DISP_SOLD is
port (EN: in std_logic;
CLK_PLACA: in std_logic;
SUMA: in integer range 0 to 9999;

-- Cathodes
L18_CA: out std_logic;
F18_CB: out std_logic;
D17_CC: out std_logic;
D16_CD: out std_logic;
G14_CE: out std_logic;
J17_CF: out std_logic;
H14_CG: out std_logic;
-- Anodes
AN0_F17: out std_logic;

AN1_H17: out std_logic;
AN2_C18: out std_logic;
AN3_F15: out std_logic);
end DISP_SOLD;

architecture ARH of DISP_SOLD is
-- digit_pattern_array = current_BCD_digit
signal digit_pattern_array : std_logic_vector(6 downto 0) := "0000000";
signal current_segment : std_logic_vector(1 downto 0) := "00";
signal cathode_select : std_logic_vector(3 downto 0) := "0000";

-- count use for the clock divider
signal count : std_logic_vector(6 downto 0) := "0000000";
-- MUX_CLK is the clock resulting from the clock divider
signal MUX_CLK : std_logic;
signal cifra_mii: std_logic_vector(3 downto 0):="0000"; -- 1st digit
signal cifra_sute: std_logic_vector(3 downto 0):="0000"; -- 2nd digit
signal cifra_zeci: std_logic_vector(3 downto 0):="0000"; -- 3rd digit
signal cifra_unitati: std_logic_vector(3 downto 0):="0000"; -- 4th digit
begin
process(EN, SUMA)

variable BIN: std_logic_vector(13 downto 0);
variable BCD: std_logic_vector(15 downto 0):=(others => '0');
variable i: integer:=0;
variable CONVERTED: std_logic:='0';
begin
if EN='1' and CONVERTED='0' then
BIN := conv_std_logic_vector(SUMA, 14);
-- Convert Binary to BCD (Double Dabble algorithm)
for i in 0 to 13 loop
bcd(15 downto 1) := bcd(14 downto 0); --shifting the bits.

bcd(0) := bin(13);
bin(13 downto 1) := bin(12 downto 0);
bin(0) :='0';

if(i < 13 and bcd(3 downto 0) > "0100") then --add 3 if BCD digit is greater than 4.
bcd(3 downto 0) := bcd(3 downto 0) + "0011";
end if;
if(i < 13 and bcd(7 downto 4) > "0100") then --add 3 if BCD digit is greater than 4.
bcd(7 downto 4) := bcd(7 downto 4) + "0011";
end if;

if(i < 13 and bcd(11 downto 8) > "0100") then --add 3 if BCD digit is greater than 4.
bcd(11 downto 8) := bcd(11 downto 8) + "0011";
end if;
if(i < 13 and bcd(15 downto 12) > "0100") then --add 3 if BCD digit is greater than 4.
bcd(15 downto 12) := bcd(15 downto 12) + "0011";
end if;
end loop;
if SUMA /= 0 then
CONVERTED:='1';
end if;

cifra_mii <= BCD(15 downto 12);
cifra_sute <= BCD(11 downto 8);
cifra_zeci <= BCD(7 downto 4);
cifra_unitati <=BCD(3 downto 0);
end if;
end process;

-- CLK_PLACA: from 50MHz to MUX_CLK (~390Hz)
divizor_CLK: process(CLK_PLACA)
begin

if rising_edge(CLK_PLACA) then
count <= count + '1';
end if;
MUX_CLK <= count(6);
end process;

variable current_BCD_digit: std_logic_vector(3 downto 0);
begin
if rising_edge(MUX_CLK) then
current_segment <= current_segment + '1';

case current_segment is
when "00" => current_BCD_digit := cifra_mii;
cathode_select <= "1110";
when "01" => current_BCD_digit := cifra_sute;
cathode_select <= "1101";
when "10" => current_BCD_digit := cifra_zeci;
cathode_select <= "1011";
when "11" => current_BCD_digit := cifra_unitati;
cathode_select <= "0111";
when others => null;

end case;

case current_BCD_digit is
when "0000" => digit_pattern_array <= "0000001";
when "0001" => digit_pattern_array <= "1001111";
when "0010" => digit_pattern_array <= "0010010";
when "0011" => digit_pattern_array <= "0000110";
when "0100" => digit_pattern_array <= "1001100";
when "0101" => digit_pattern_array <= "0100100";
when "0110" => digit_pattern_array <= "0100000";

when "0111" => digit_pattern_array <= "0001111";
when "1000" => digit_pattern_array <= "0000000";
when "1001" => digit_pattern_array <= "0001100";
when others => null;
end case;
end if;
end process;
L18_CA <= digit_pattern_array(6);
F18_CB <= digit_pattern_array(5);
D17_CC <= digit_pattern_array(4);

D16_CD <= digit_pattern_array(3);
G14_CE <= digit_pattern_array(2);
J17_CF <= digit_pattern_array(1);
H14_CG <= digit_pattern_array(0);

AN0_F17 <= cathode_select(0) when EN='1' else '0';
AN1_H17 <= cathode_select(1) when EN='1' else '0';
AN2_C18 <= cathode_select(2) when EN='1' else '0';
AN3_F15 <= cathode_select(3) when EN='1' else '0';
end ARH;


Answer



In VHDL (and HDLs in general) a for loop does not denote sequential execution like it does in a software programming language — it denotes the construction of multiple parallel instances of the hardware described in the body of the loop.


In your case, you have many assignments to the same variable BCD/bcd, and these are conflicting with each other. If you really intend to construct a system based on two shift registers (one binary, one bcd) that takes 14 clock periods to do the conversion, then you need to describe the hardware that way, and set up a state machine to control it.


On the other hand, if you really want to do it entirely combinatorially, then you need to create different intermediate variables (e.g., arrays that are indexed by the loop control variable) to hold the results at each stage.


No comments:

Post a Comment

arduino - Can I use TI&#39;s cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...