VHDLStarter
Newbie level 6
Hey there, I am having problems modifying my basic division code to an optimal solution using state machines. The simple state machine design is basically to subtract at each clock cycle. I have an example of it below:
The above works just fine. I posted it to give you an idea of what the previous solution was. However, the optimal solution revolves around either a series of if statements or for loops to compare the dividend and the divisor. This is where I start to get confused.
My current idea is to follow this method with some modifications for binary:
About Assembly Language - Division
You have to shift it over in decimal or basically multiply by 10 on the start. I tried to use * and "and" to multiply, but that did not work. I managed to get around this by shifting the bits over by 3 then adding the divisor * 2.
ex:
dividend/divisor = divisor shift left 3 times + divisior *2 = divisor * 10
4/2 -> divisor in binary = 10 -> 10000 -> (10000 + 10 +10) = 10100 = 20
As you can see I successfully multiplied the divisor by 10 using this method. However, after I do that I need to judge to see if I need to go back to the previous version of the divisor or not. After the value of the divisor is determined I would follow the steps in the above description with two separate counters. After each counter is finished I would have to combine the numbers. I could do this with an if statement determining if the second counter is greater than a number.
EX. if counter is greater than a certain value then shift a certain amount of times and add to first counter, if it is less than just shift x amount of times and add to first counter, etc. That is my reasoning behind combining the two different counts in the above description.
Unfortunately, I am having a lot of trouble implementing my logic for the optimal divison. Below is code that isn't finished with a lot of comments (WIP):
Is there an easier way to do this? If not, can someone maybe help me get to the desired optimal level of division? Thank you.
Code:
library IEEE;
use IEEE.std_logic_1164.all;
use ieee.numeric_std.all;
--use ieee.std_logic_arith.all;
use Ieee.std_logic_unsigned.all;
entity div is
Port (dividend, divisor : in std_logic_vector(9 downto 0);
RSTb, CLK : in std_logic;
quotient, remainder : out std_logic_vector(9 downto 0));
end div;
architecture div_arch of div is
type STATE_TYPE is (s0, s1, s2);
signal CURRENT_STATE, NEXT_STATE: STATE_TYPE;
signal ONE : unsigned (9 downto 0):="0000000001";
signal next_dividend, current_dividend, next_remainder, next_quotient, current_quotient : std_logic_vector (9 downto 0):="0000000000";
begin
-- Process to hold combinational logic
process(clk,rstb,dividend,divisor,next_remainder,next_quotient,Next_state,next_dividend, CURRENT_STATE)
begin
case current_state is
when s0 => if(unsigned(current_dividend) > unsigned(divisor)) then
next_state <= s1;
elsif(unsigned(current_dividend) = unsigned(divisor))then
next_remainder <= "0000000000";
next_state <= s0;
elsif (unsigned(current_dividend) < unsigned(divisor))then
next_remainder <= "0000000000";
next_state <= s0;
else
next_state <= s0;
end if;
current_dividend<=dividend;
next_dividend <= dividend;
next_quotient <= "0000000000";
current_quotient <= "0000000000";
next_remainder <= "0000000000";
when s1 => if(unsigned(current_dividend) >= unsigned(divisor)) then
next_state <= s1;
next_dividend <= std_logic_vector(unsigned(current_dividend) - unsigned(divisor));
next_quotient <= std_logic_vector(unsigned(current_quotient) + unsigned(one));
else
next_remainder <= current_dividend;
next_state <= s2;
end if;
when s2 => --dead state to wait for reset. Not really needed.
end case;
--asynchronous, active low
if(rstb = '0') then
current_state <= s0;
quotient <= "0000000000";
remainder <= "0000000000";
current_dividend <= dividend;
next_dividend <= dividend;
elsif(clk'event and clk = '1') then
current_state <= next_state;
current_dividend<=next_dividend;
current_quotient <= next_quotient;
quotient <= current_quotient;
remainder <= next_remainder;
end if;
end process;
end div_arch;
configuration div_arch_cfg of div is
for div_arch
end for;
end div_arch_cfg;
The above works just fine. I posted it to give you an idea of what the previous solution was. However, the optimal solution revolves around either a series of if statements or for loops to compare the dividend and the divisor. This is where I start to get confused.
My current idea is to follow this method with some modifications for binary:
In practice, when dividing a large number by a much smaller number, you would shift the smaller number one or more places to the left by setting it up on the dials to the left of where it would normally be, leaving the right-hand dials set to zero. The degree to which you shift it would be dictated by common sense and mental arithmetic - if you can't subtract it even once from the other number, then you have gone too far! You would then subtract this shifted number as many times as possible to obtain the first digit of the result, reset all your dials to shift it back to the right, repeatedly subtract again to find the next digit, and continue this process until you reach your original number again and can no longer subtract even that.
Using our example of 128÷4, you would end up doing this:
Shift 4 one place to the left - it becomes 40.
Shift 4 another place to the left - it becomes 400. This is bigger than 128, so we have gone too far. We shall start with the previous result, which was 40. So:
128 - 40 = 88 (turning the handle once)
88 - 40 = 48 (turning it twice)
48 - 40 = 8 (turning it three times)
We can no longer subtract 40, so we have now found the first digit of the answer to be 3.
Now we shift our number back to the right so that it becomes 4 again, and once more start counting turns of the handle.
8 - 4 = 4 (turning the handle once)
4 - 4 = 0 (turning it twice)
We can no longer subtract 4 (our original number), so we have found the last digit of the answer to be 2. In other words, the answer is 32, the result we got before - but we have had to wind the handle round only five times in order to obtain it, not thirty-two times!
About Assembly Language - Division
You have to shift it over in decimal or basically multiply by 10 on the start. I tried to use * and "and" to multiply, but that did not work. I managed to get around this by shifting the bits over by 3 then adding the divisor * 2.
ex:
dividend/divisor = divisor shift left 3 times + divisior *2 = divisor * 10
4/2 -> divisor in binary = 10 -> 10000 -> (10000 + 10 +10) = 10100 = 20
As you can see I successfully multiplied the divisor by 10 using this method. However, after I do that I need to judge to see if I need to go back to the previous version of the divisor or not. After the value of the divisor is determined I would follow the steps in the above description with two separate counters. After each counter is finished I would have to combine the numbers. I could do this with an if statement determining if the second counter is greater than a number.
EX. if counter is greater than a certain value then shift a certain amount of times and add to first counter, if it is less than just shift x amount of times and add to first counter, etc. That is my reasoning behind combining the two different counts in the above description.
Unfortunately, I am having a lot of trouble implementing my logic for the optimal divison. Below is code that isn't finished with a lot of comments (WIP):
Code:
library IEEE;
use IEEE.std_logic_1164.all;
use ieee.numeric_std.all;
--use ieee.std_logic_arith.all;
use Ieee.std_logic_unsigned.all;
entity div is
Port (dividend, divisor : in std_logic_vector(9 downto 0);
RSTb, CLK : in std_logic;
quotient, remainder : out std_logic_vector(9 downto 0));
end div;
architecture div_arch of div is
type STATE_TYPE is (s0, s1, s2);
signal CURRENT_STATE, NEXT_STATE: STATE_TYPE;
signal ONE : unsigned (9 downto 0):="0000000001";
signal TWO : unsigned (9 downto 0):="0000000010";
signal current_divisor,storage,count,count2,next_divisor,next_dividend, current_dividend, next_remainder, next_quotient, current_quotient : std_logic_vector (9 downto 0):="0000000000";
signal shiftC : integer range 0 to 1024 := 0;
--signal onec : std_logic_vector (9 downto 0):="0000000000";
signal temp,temp2,temp3 : std_logic_vector (9 downto 0);
--signal tf : boolean := true;
--function
--function shiftL (divisor, i : std_logic_vector(0 downto 9)) return std_logic_vector is
-- result : std_logic_vector(0 downto 9);
-- begin
-- result <= divisor * i;
--return(result);
--end shiftL;
--end function
--procedure shiftL (divisor, i : std_logic_vector(0 downto 9)) is
--begin
-- end procedures shiftL ;
begin
-- Process to hold combinational logic
process(clk,rstb,dividend,divisor,next_remainder,next_quotient,Next_state,next_dividend, CURRENT_STATE)
begin
case current_state is
when s0 => if(unsigned(current_dividend) > unsigned(divisor)) then
next_state <= s1;
elsif(unsigned(current_dividend) = unsigned(divisor))then
next_remainder <= "0000000000";
next_state <= s0;
elsif (unsigned(current_dividend) < unsigned(divisor))then
next_remainder <= "0000000000";
next_state <= s0;
else
next_state <= s0;
end if;
current_dividend<=dividend;
next_dividend <= dividend;
current_divisor<=divisor;
next_divisor <= divisor;
next_quotient <= "0000000000";
current_quotient <= "0000000000";
next_remainder <= "0000000000";
temp <= "0000000000";
temp2 <= "0000000000";
temp3 <= next_divisor;
when s1 =>
if(unsigned(dividend) >= unsigned(current_divisor)) then
next_state <= s1;
--starting calc to shift
for i in 0 to 9 loop
if(unsigned(dividend) >= unsigned(temp)) then
--temp3 <= next_divisor;
next_divisor <= std_logic_vector(unsigned(current_divisor) sll 3);
temp2 <= std_logic_vector(unsigned(divisor) + unsigned(divisor));
temp <= std_logic_vector(unsigned(next_divisor) + unsigned(temp2));
--next_divisor <= "0000000000";
else
exit;
end if;
end loop;
--we need to go back one mutliple if we go over
--next_divisor <= temp;
--next_divisor <= temp;
--next_divisor <= std_logic_vector(unsigned(current_divisor) srl 3);
--temp2 <= std_logic_vector(unsigned(divisor) - unsigned(divisor));
-- temp <= std_logic_vector(unsigned(next_divisor) - unsigned(temp2));
if(unsigned(dividend) >= unsigned(current_divisor)) then
end if;
--storage signal
storage <= current_dividend;
--wind up
--for i in 0 to 9 loop
-- if(storage < current_divisor) then
-- exit;
-- end if;
-- storage <= storage - current_divisor;
--count <= std_logic_vector(unsigned(count) + 1);
-- end loop;
----wind down
--for i in 0 to 9 loop
-- if(storage < current_quotient) then
-- exit;
-- end if;
-- storage <= storage - next_divisor;
-- count2 <= std_logic_vector(unsigned(count2) + 1);
--end loop;
--if(unsigned(dividend) >= unsigned(current_divisor)) then
--next_dividend <= std_logic_vector(unsigned(current_dividend) - unsigned(next_divisor));
--next_quotient <= std_logic_vector(unsigned(current_quotient) + unsigned(one));
--end if;
--end loop;
else
--next_remainder <= current_dividend;
--next_state <= s2;
end if;
when s2 => --Dead state to wait for reset
end case;
--asynchronous, active low
if(rstb = '0') then
current_state <= s0;
quotient <= "0000000000";
remainder <= "0000000000";
count <= "0000000000";
count2 <= "0000000000";
current_dividend <= dividend;
next_dividend <= dividend;
current_divisor<=divisor;
next_divisor <= divisor;
elsif(clk'event and clk = '1') then
current_state <= next_state;
current_dividend<=next_dividend;
current_quotient <= next_quotient;
current_divisor <= next_divisor;
quotient <= current_quotient;
remainder <= next_remainder;
end if;
end process;
end div_arch;
configuration div_arch_cfg of div is
for div_arch
end for;
end div_arch_cfg;
Is there an easier way to do this? If not, can someone maybe help me get to the desired optimal level of division? Thank you.
Last edited: