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\$

2 Answers 2

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\$
1
  • \$\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\$ Commented Jan 28, 2015 at 23:47

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.