1

I am quite a rookie in the VHDL world, but I seem to have hold of the basics. Atm I am working on a project, which involves me to take a 4-bit binary input (switches), read this value and convert it to decimal values.

This far I have variables (count_ones, count_tens) which are supposed to contain the decimal value of the input.

My current code is as following:

architecture Behavioral of Seven_Seg_Counter is

    signal      count_ones      :   integer range 0 to 9 := 0;
    signal      count_tens      :   integer range 0 to 9 := 0;
    signal      count_hundreds  :   integer range 0 to 9 := 0;
    signal      count_thousands :   integer range 0 to 9 := 0;
    signal      prescaler       :   integer := 0;
    signal      anode           :   integer range 1 to 4 := 1;
    signal      digit           :   STD_LOGIC_VECTOR(3 downto 0) := "1110";

begin
    Calculate: process (CLK)
        variable        countings           :   integer range 0 to 9 := 0;

    begin
        if rising_edge(CLK) then
            if INPUT(3) = '1' then
                countings := 8;
            else
                countings := 0;
            end if;
            if INPUT(2) = '1' then
                countings := countings + 4;
            else
                countings := 0;
            end if;
            if INPUT(1) = '1' then
                countings := countings + 2;
            else
                countings := 0;
            end if;
            if INPUT(0) = '1' then
                countings := countings + 1;
            else
                countings := 0;
            end if;

            for I in 1 to 9 loop
                if countings > 9 then
                    countings := countings  - 10;
                    count_tens <= count_tens + 1;
                else
                    count_tens <= 0;
                end if;
            end loop;

            for G in 1 to 9 loop
                if countings > 0 then
                    countings := countings - 1;
                    count_ones <= count_ones + 1;
                else
                    count_ones <= 0;
                end if;
            end loop;
        end if;
    end process;

    process (anode, count_thousands, count_hundreds, count_tens, count_ones) -- Bestemmer output som funktion signalerne anode (segmentvælger) og count_XX (den decimale værdi)
    begin
        case anode is
            when 1 => 
                case count_ones is
                                          --abcdefg
                    when 0 => sseg <= "0000001"; -- 0
                    when 1 => sseg <= "1001111"; -- 1
                    when 2 => sseg <= "0010010"; -- 2
                    when 3 => sseg <= "0000110"; -- 3
                    when 4 => sseg <= "1001100"; -- 4
                    when 5 => sseg <= "0100100"; -- 5
                    when 6 => sseg <= "0100000"; -- 6
                    when 7 => sseg <= "0001111"; -- 7
                    when 8 => sseg <= "0000000"; -- 8
                    when 9 => sseg <= "0000100"; -- 9
                end case;
            when 2 => case count_tens is
                                          --abcdefg
                    when 0 => sseg <= "0000001"; -- 0
                    when 1 => sseg <= "1001111"; -- 1
                    when 2 => sseg <= "0010010"; -- 2
                    when 3 => sseg <= "0000110"; -- 3
                    when 4 => sseg <= "1001100"; -- 4
                    when 5 => sseg <= "0100100"; -- 5
                    when 6 => sseg <= "0100000"; -- 6
                    when 7 => sseg <= "0001111"; -- 7
                    when 8 => sseg <= "0000000"; -- 8
                    when 9 => sseg <= "0000100"; -- 9
                end case;
            when 3 => case count_hundreds is
                                          --abcdefg
                    when 0 => sseg <= "0000001"; -- 0
                    when 1 => sseg <= "1001111"; -- 1
                    when 2 => sseg <= "0010010"; -- 2
                    when 3 => sseg <= "0000110"; -- 3
                    when 4 => sseg <= "1001100"; -- 4
                    when 5 => sseg <= "0100100"; -- 5
                    when 6 => sseg <= "0100000"; -- 6
                    when 7 => sseg <= "0001111"; -- 7
                    when 8 => sseg <= "0000000"; -- 8
                    when 9 => sseg <= "0000100"; -- 9
                end case;
            when 4 =>  case count_thousands is
                                          --abcdefg
                    when 0 => sseg <= "0000001"; -- 0
                    when 1 => sseg <= "1001111"; -- 1
                    when 2 => sseg <= "0010010"; -- 2
                    when 3 => sseg <= "0000110"; -- 3
                    when 4 => sseg <= "1001100"; -- 4
                    when 5 => sseg <= "0100100"; -- 5
                    when 6 => sseg <= "0100000"; -- 6
                    when 7 => sseg <= "0001111"; -- 7
                    when 8 => sseg <= "0000000"; -- 8
                    when 9 => sseg <= "0000100"; -- 9
                end case;
        end case;
    end process;

    process (anode)
    begin
        case anode is
            when 1 => digit <= "1110";
            when 2 => digit <= "1101";
            when 3 => digit <= "1011";
            when 4 => digit <= "0111";
        end case;
    end process;

    segmentselect <= digit;
    LED_OUT <= INPUT;
end Behavioral;

My idea is, I store the binary value (looking aside 1's and 2's complement) in the variable countings (which works fine in a simulation). The next part is, where I actually find the value of countings, and store it's rightmost digit in the signal count_ones (ones), and the leftmost digit (tens) in the signal count_tens. For some unknown reason, my way of finding the ones and tens does not work well with those loops. Maybe I totally misunderstood how to use for loops.

Could any of you maybe help me out? I would really appreciate it.


Loops are tricky indeed. The thing is, a 4-bit input is only the beginning, I'd like to be able to read more, so if I can learn the basic method with just 4 bits, that'd be awesome.

Well, the count_ones signal is for the ones, and the count_tens is for the tens of the input, so I am not quite sure I get, how to_integer(unsigned(INPUT)); is gonna work out, so I get a decimal value from 0 to 9 in count_ones or count_tens.

The subtraction method you are referencing to, I believe, I have tried to implement that in my loops, which do not work out as expected.

Dave Tweed
  • 168,369
  • 17
  • 228
  • 393

2 Answers2

1

Loops in HDL are a tricky thing to use in a synthesizable manner. I would recommend simply reading in the input bits as a binary number, compare with 9, if it is greater subtract 10.

alex.forencich
  • 40,694
  • 1
  • 68
  • 109
1

Solution 1:
The Double Dabble Algorithm. Electronics.StackExchange lists many Q&A for this topic:

StackOverflow is also full of results.

Solution 2
Using a systolic array of digit converts.

Let's convert a n-bit binary input into m digits. Each systolic element has a Clock and Reset input as well as a 1-bit Shift_in signal. It generates a 4-bit BCD encoded Digit and a Shift_out signal (aka overflow).

This is the element's port declaration:

subtype T_BCD : UNSIGNED(3 downto 0);

entity arith_convert_bin2bcd_element is
  port (
    Clock      : IN  STD_LOGIC;
    Reset      : IN  STD_LOGIC;

    Shift_en   : IN  STD_LOGIC;
    Shift_in   : IN  STD_LOGIC;
    Shift_out  : OUT STD_LOGIC;
    BCD        : OUT T_BCD
  );
end;

How does it work?

On every shift operation, it calculates:
\$Digit = Digit * 2 + Shift_{in}\$

\$Shift_{out} = \begin{cases} 1 &\text{when}\,\, (Digit \ge 10) \\ 0 & else\end{cases}\$

\$Digit = \begin{cases} Digit-10 &\text{when}\,\, (Digit \ge 10) \\ Digit & else\end{cases}\$

architecture rtl of arith_convert_bin2bcd_element is
  signal Digit_ext  : UNSIGNED(T_BCD'length downto 0);
  signal Digit_ov   : STD_LOGIC;
  signal Digit_d    : T_BCD        := "0000";
begin

  Digit_ext  <= Digit_d & Shift_in;
  Digit_ov  <= to_sl(Digit_ext > 9);
  Shift_out  <= Digit_ov;-- when rising_edge(Clock);    -- register overflow

  process(Clock)
  begin
    if rising_edge(Clock) then
      if (Reset = '1') then
        Digit_d    <= "0000";
      elsif (Shift_en = '1') then
        if (Digit_ov = '0') then
          Digit_d  <= Digit_ext(Digit_d'range);
        else
          Digit_d  <= resize(Digit_ext - 10, Digit_d'length);
        end if;
      end if;
    end if;
  end process;

  BCD <= Digit_d;
end;

Now you can combine as many element as needed to convert a binary number. What's needed for this task?

  • a FSM with 3-4 states: idle, [complement], convert, complete
    complement is only needed if you input can be negative
  • a counter that counts for n shift operations
  • a parallel-in/serial-out register, that shifts or rotates left (to the highest bit)
  • a generate statement, that generates m element instances

And here is the systolic array wiring and control logic:

entity arith_convert_bin2bcd is
  generic (
    IS_SIGNED     : BOOLEAN     := FALSE;
    BITS          : POSITIVE    := 8;
    DIGITS        : POSITIVE    := 3
  );
  port (
    Clock         : IN  STD_LOGIC;
    Reset         : IN  STD_LOGIC;

    Start         : IN  STD_LOGIC;
    Complete      : OUT STD_LOGIC;

    Binary        : IN  STD_LOGIC_VECTOR(BITS - 1 DOWNTO 0);
    BCDDigits     : OUT T_BCD_VECTOR(DIGITS - 1 DOWNTO 0);
    Sign          : OUT STD_LOGIC
  );
end;

architecture rtl of arith_convert_bin2bcd is
  type T_STATE is (ST_IDLE, ST_CPL, ST_CONVERT, ST_COMPLETE);

  signal State      : T_STATE    := ST_IDLE;
  signal NextState  : T_STATE;

  signal Digit_Shift_en   : STD_LOGIC;
  signal Digit_Shift_in   : STD_LOGIC_VECTOR(DIGITS downto 0);

  signal Binary_en        : STD_LOGIC;
  signal Binary_cpl       : STD_LOGIC;
  signal Binary_rl        : STD_LOGIC;
  signal Binary_d         : STD_LOGIC_VECTOR(BITS - 1 downto 0)      := (others => '0');
  signal Binary_Sign      : STD_LOGIC;

  signal Sign_d           : STD_LOGIC                                := '0';

  signal ShiftCounter_rst : STD_LOGIC;
  signal ShiftCounter_eq  : STD_LOGIC;
  signal ShiftCounter_s   : SIGNED(log2ceilnz(BITS - 1) downto 0)    := (others => '0');
begin

  process(Clock)
  begin
    if rising_edge(Clock) then
      if (Reset = '1') then
        State    <= ST_IDLE;
      else
        State    <= NextState;
      end if;
    end if;
  end process;

  process(State, Start, Binary_Sign, ShiftCounter_eq)
  begin
    NextState         <= State;
    Complete          <= '0';

    Binary_en         <= '0';
    Binary_cpl        <= '0';
    Binary_rl         <= '0';

    Digit_Shift_en    <= '0';
    ShiftCounter_rst  <= '1';

    case State is
      when ST_IDLE =>
        if (Start = '1') then
          Binary_en       <= '1';

          if (Binary_Sign = '1') then
            NextState     <= ST_CPL;
          else
            NextState     <= ST_CONVERT;
          end if;
        end if;

      when ST_CPL =>
        Binary_cpl        <= '1';
        NextState         <= ST_CONVERT;

      when ST_CONVERT =>
        Binary_rl         <= '1';
        Digit_Shift_en    <= '1';
        ShiftCounter_rst  <= '0';

        if (ShiftCounter_eq = '1') then
          NextState       <= ST_COMPLETE;
        end if;

      when ST_COMPLETE =>
        Complete          <= '1';
        NextState         <= ST_IDLE;

    end case;
  end process;

  process(Clock)
  begin
    if rising_edge(Clock) then
      if (Reset = '1') then
        Binary_d  <= (others => '0');
      elsif (Binary_en = '1') then
        Binary_d  <= Binary;
        Sign_d    <= '0';
      elsif (Binary_cpl = '1') then
        Binary_d  <= cpl(Binary_d);    -- 2's component
        Sign_d    <= '1';
      elsif (Binary_rl = '1') then
        Binary_d  <= Binary_d(Binary_d'high - 1 downto 0) & Binary_d(Binary_d'high);
      end if;
    end if;
  end process;

  Sign              <= Sign_d;

  Binary_Sign       <= Binary_d(Binary_d'high);
  Digit_Shift_in(0) <= Binary_d(Binary_d'high);

  -- count shift operations from BITS-2 downto -1
  ShiftCounter_s    <= counter_dec(ShiftCounter_s, ShiftCounter_rst, '1', BITS - 2) when rising_edge(Clock);
  ShiftCounter_eq   <= ShiftCounter_s(ShiftCounter_s'high);

  -- generate DIGITS many systolic elements
  genDigits : for i in 0 to DIGITS - 1 generate
   Digit : entity PoC.arith_convert_bin2bcd_element
    port map (
      Clock       => Clock,
      Reset       => Reset,

      Shift_en    => Digit_Shift_en,
      Shift_in    => Digit_Shift_in(i),
      Shift_out   => Digit_Shift_in(i + 1),
      BCD         => BCDDigits(i)
    );
  end generate;
end;

And here is a simulation (clickable):

Paebbels
  • 3,897
  • 2
  • 18
  • 43
  • Okay, I recently read up on your comment. I believe what you suggest, is what my countings variable takes care off. It stores the decimal value of my 4-bit binary input (INPUT). I need to store countings' ones in a variable called count_ones, and its tens in a variable called count_tens, same ultimately goes for hundreds and thousands as well (see my comment below). – Monsieur Cascode Jan 28 '15 at 23:47