1
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

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):

\$\endgroup\$
  • \$\begingroup\$ 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). \$\endgroup\$ – Jacob Overgaard Jan 28 '15 at 23:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.