.
These happen because no input is defined in the module, but given in the testbench. The same goes for the output1 and output2, where the values will come up after the module gets some inputs.
Figure 10a. Simulation Result (input-output data)
Figure 10b. Simulation Result (objects values)
The LUT operates as planned where the system is given a certain input, it will go to a certain address (which is converted from vector to integer since the LUT was made using an array with integer index as seen as address and t_addr respectively in figure 10b), iterates through the number of sequences (represented by the signal count_val in figure 10b), and returns the value in each address.
The only issue is that the simulation didn’t show the value in each address during the iteration, instead it only shows the value in the last address. For example:
-
sensor value = 1010
-
count_val (number of iteration/sequence) = 5
-
starting address (vector) = 10100000
-
starting address (integer) = 160
-
address range : 160, 161, 162, 163, 164
-
address values:
Address
|
Value
|
Output1
|
Output2
|
160
|
1111
|
1111
|
161
|
1111
|
1111
|
162
|
1110
|
0000
|
163
|
0000
|
0000
|
164
|
1111
|
1111
|
The simulation went through sensor value: 1111, 0001, and 1010.
This problem should be resolved in future work.
Conclusions
Like all other things, the LUT has its issues and limitations. First, depending on the amount, type of movements, and addressing scheme, it requires quite some amount of memory space. Imagine the robot is a robotic arm and it has 6 joints, each joint has 1 degree of freedom (DOF) and its value range 0-255 (8 bits), thus if we put the position of all joints in every row of the LUT, we have 48 bits (6 bytes) of data in every row. Now imagine if we have a humanoid robot, with, let’s say, 20 DOF. See how the memory width (number of bits) can increase rapidly?
Next, imagine that we simplify the LUT address such that each address bit represents a sensor reading. If we have 4 touch sensors, we have 4 bits of LUT address, with some extra bits for the sequences. So, if we have 8 sequences of movement, we need 3 more bits, making the address to be 7 bits each; which requires the memory to be at least 128 rows long or 128 x 6 bytes = 768 bytes. Now imagine that we have 4 sensors that each has value range of 0-255 (8 bits). Now we need 4x8bits=32 bit address, which is about 4 Gigs of memory!
This is one major issue for LUT for robotics use; it requires a lot of memory space. However, currently large-sized memories are readily available with affordable price, and the capacity should double every two years, and memory prices drop a faster rate, hopefully this will not be such an issue in the future.
Note that the above issue assumes a limited number of movement sequences (i.e. 8 sequences), and it is possible that there are a lot of redundant values, and/or empty spaces (i.e. movements that has less than 8 sequences). Another way that could be explored is by having multiple levels of LUT. For example, we have a LUT containing some set of predefined joint/limb positions. On a higher level we have another LUT, containing the sequence of movements, but instead of storing the positions of each joint, it contains the list of addresses for the LUT that contains the predefined positions. This way it can avoid using one huge block of memory by splitting it into smaller blocks (although it can still physically be in one memory module).
This is a bit of a trade-off, to enable faster response at the expense of larger memory space. But generally, by putting the movement in the LUT, the response time of the robot is most likely to be much faster than having to calculate each movement every time.
Future Work
For future work, a function could be added to generate the movements automatically, and save it in the LUT. This would require some kind of fitness function evaluation to make sure that the movement that is saved in the LUT given a certain input values is the best one.
As mentioned earlier, such movement function might require some sine/cosine function. Since VHDL doesn’t have a built-in syntax to calculate sine/cosine (at least not the in version used to do this assignment), it is possible to create a separate LUT for each of those functions which then can be used to do the movement functions.
Another thing that could be added to this LUT method is a strategy/behavior selection mechanism, which may include artificial intelligence, or genetic algorithm, or some selection scheme.
There is also the possibility to explore different addressing scheme for the LUT. The nature of the LUT addressing scheme makes it similar to those of caches, and in the interrupt mechanisms in some microprocessors.
Other considerations:
Is it possible to override the current running sequence if there’s a change in the input? Maybe if it can detect an abrupt change, then it can decide whether or not to override. The design in this assignment seems to allow the iteration to be inhibited if given a different sensor values while it is still running. This can also be explored further in future works.
Appendix A
VHDL Code
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity table_guy is
Port ( sensor : in std_logic_vector(3 downto 0);
--addr : in integer range 0 to 255;
--clk, reset: in std_logic;
--t_addr : out integer range 0 to 255;
output1 : out std_logic_vector(3 downto 0);
output2 : out std_logic_vector(3 downto 0));
end table_guy;
architecture Behavioral of table_guy is
type t_mem_data is array (0 to 255) of std_logic_vector(7 downto 0);
constant data:t_mem_data:= (
"11111110","10000000","01111111","10000000","11111111","10000000","11111111","10000000",
"11111110","01100000","01111111","01100000","11111111","01100000","11111111","01100000",
"01111111","00100000","11111110","01000000","11111111","00100000","11111111","00100000",
"01111110","00100000","11111110","01000000","11111111","00100000","11111111","00100000",
"00111111","10010001","11111100","10000000","11111111","00100000","11111111","00100000",
"00111111","10010001","11111100","10000000","11111111","00100000","11111111","00100000",
"00011111","11001011","11111001","00000000","11111111","11111111","11111111","00100000",
"00011111","11001011","11111001","00000000","11111111","11111111","11111111","00100000",
"00001111","11100111","11110010","00000000","11111111","11111111","11111111","00100000",
"00001111","11100111","11110010","00000000","11111111","11111111","11111111","00100000",
"00000111","11111111","11100100","00000000","11111111","00000000","11111111","00100000",
"00000111","11111111","11100100","00000000","11111111","00111111","11111111","00100000",
"00000011","11111111","11001000","00000000","11111111","00100000","11111111","00100000",
"00000011","11111111","11001000","00000000","11111111","00100000","11111111","00100000",
"00000001","11111111","10010000","00000000","11111111","00100000","11111111","00100000",
"00000000","11111111","00100000","00000000","11111111","00100000","11111111","00100000",
"11111111","11111100","00000000","00000000","11111111","00000000","00000000","00000000",
"11111111","11111110","11000000","00000000","11111111","10000000","00000000","00000000",
"11111111","11111111","11100000","00000000","11111111","01000000","00000000","00000000",
"11111111","11111111","11110000","00000000","11111111","00100000","00000000","00000000",
"11111111","00000000","11111100","00000000","11111111","00100000","00000000","00000000",
"11111111","00111111","11111111","00000000","11111111","00100000","00000000","00000000",
"11111111","00100000","00111111","10000000","11111111","00100000","00000000","00000000",
"11111111","00100000","00111111","01000000","11111111","00100000","00000000","00000000",
"11111111","00100000","00111111","01000000","11111111","00100000","00000000","00000000",
"11111111","00100000","00111111","01000000","11111111","00100000","00000000","00000000",
"11111111","00100000","00111111","01000000","11111111","00100000","00000000","00000000",
"11111111","00100000","11111111","10000000","11111111","00100000","00000000","00000000",
"11111111","11111111","11111101","00000000","11111111","11111111","11111111","00000000",
"11111111","11111111","11110010","00000000","11111111","11111111","11111111","10000000",
"11111111","11111110","11100100","00000000","11111111","11111111","11111111","00100000",
"11111111","11111100","11001000","00000000","11111111","11111111","11111111","00100000");
-- this is sample data
signal count_val: integer range 0 to 16
signal address: std_logic_vector(7 downto 0);
signal t_addr: integer range 0 to 255;
-- integer to vector conversion function
function integer2vector(value:integer) return std_logic_vector is
variable vector: std_logic_vector(7 downto 0);
variable q:integer;
begin
q:=value;
for i in 7 downto 0 loop
if((q mod 2)=1) then
vector(i):='1';
else
vector(i):='0';
end if;
q:=q/2;
end loop;
return vector;
end integer2vector;
-- vector/binary to integer function
function binary2integer(alpha: std_logic_vector) return integer is
variable result: integer:=0;
alias av: std_logic_vector(alpha'length downto 1) is alpha;
variable b: integer:=1;
--variable idx: integer;
begin
for n in 1 to alpha'length loop
--idx:=n-1;
if(av(N)='1') then
result:=result+b;
else
result:=result;
end if;
b:=b*2;
end loop;
return result;
end binary2integer;
begin
control:process (sensor)
begin
case (sensor) is
when "0001" => count_val <= 16; -- can only have max 16 steps?
when "0010" => count_val <= 12;
when "0011" => count_val <= 7;
when "0100" => count_val <= 10;
when "0101" => count_val <= 16;
when "0110" => count_val <= 12;
when "0111" => count_val <= 13;
when "1000" => count_val <= 9;
when "1001" => count_val <= 4;
when "1010" => count_val <= 5;
when "1011" => count_val <= 10;
when "1100" => count_val <= 12;
when "1101" => count_val <= 8;
when "1110" => count_val <= 16;
when "1111" => count_val <= 12;
when others => count_val <= 0;
end case;
end process control;
executor: process(sensor, address, count_val)
variable t_address:integer range 0 to 256;
variable t_data: std_logic_vector(7 downto 0);
variable k,count: integer range 1 to 17;
begin
address <= sensor&"0000"; -- meaning, it always starts at address xxxx0000 (modulo 16), -- where the last four "0"s is used to count the sequence...
t_addr<=binary2integer(address);
t_address:=t_addr;
if (sensor="0000") then
output1<="0000";
output2<="0000";
else
k:=1;
while(k<=count_val) loop -- iterate through the sequences
t_data:=data(t_address);
output1 <= t_data(7 downto 4);
output2 <= t_data(3 downto 0);
t_address:=t_address+1; -- next address
k:=k+1;
end loop;
end if;
end process executor;
end Behavioral;
Appendix B
Testbench Code
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_unsigned.all;
USE ieee.numeric_std.ALL;
ENTITY lut_tb_vhd IS
END lut_tb_vhd;
ARCHITECTURE behavior OF lut_tb_vhd IS
-- Component Declaration for the Unit Under Test (UUT)
COMPONENT table_guy
PORT(
sensor : IN std_logic_vector(3 downto 0);
output1 : OUT std_logic_vector(3 downto 0);
output2 : OUT std_logic_vector(3 downto 0)
);
END COMPONENT;
--Inputs
SIGNAL sensor : std_logic_vector(3 downto 0) := (others=>'0');
--Outputs
SIGNAL output1 : std_logic_vector(3 downto 0);
SIGNAL output2 : std_logic_vector(3 downto 0);
BEGIN
-- Instantiate the Unit Under Test (UUT)
uut: table_guy PORT MAP(
sensor => sensor,
output1 => output1,
output2 => output2
);
tb : PROCESS
constant interval: time:=300 ns;
BEGIN
-- Wait 100 ns for global reset to finish
wait for 100 ns;
-- Place stimulus here
sensor <= "1111";
wait for interval;
sensor <= "0001";
wait for interval;
sensor <= "1010";
--wait; -- will wait forever
END PROCESS;
END;