I know what the error means and why it's bad, but can't figure out how to do it in other way.
Can't resolve multiple constant drivers for net "snake[17]" at snake_driver.
(and others the same)
The assignment is that we have a "moving snake" in a std_logic_vector, which goes end-to-end, and when you press a button (toggle signal), the snake changes it's length (2, 3, 4, 5, 6, 2, ...)
Obviously, the snake vector must therefore be changed by both a process listening for toggle, and the one listening for clock. When I put both in the same process, I got an error that two edge detects can't be in the same process.
/\____ +--------------------+
toggle ----> change length |
| v |
| [snake register] =====> snake 17 downto 0
\/\/\/ | ^ |
clock ----> move one step |
+--------------------+
Any ideas welcome.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity snake_driver is
generic
(
LEN : integer := 18;
MAX_SNAKE_LEN : integer := 6
);
port
(
clk : in std_logic;
change : in std_logic;
output : out std_logic_vector(LEN-1 downto 0)
);
end snake_driver;
architecture Anaconda of snake_driver is
signal snake : std_logic_vector(LEN-1 downto 0) := (0 => '1', 1 => '1', others => '0'); -- two snake pieces
signal dir_right : boolean := FALSE; -- start left
begin
process(change)
variable data : std_logic_vector(LEN-1 downto 0); -- two snake pieces
variable snake_len : integer := 2; -- snake 2 long
begin
if rising_edge(change) then -- change snake length
-- add snake piece based on direction
data := snake; -- <-- here I tried to avoid the problem
-- by caching snake data. To no avail.
if snake_len = MAX_SNAKE_LEN then
snake_len := 2;
-- shorten to 2 len
if dir_right then
-- moving right, remove from left
data := std_logic_vector(unsigned(data) and shift_right(unsigned(data), MAX_SNAKE_LEN-2));
else
-- moving left, remove from right
data := std_logic_vector(unsigned(data) and shift_left(unsigned(data), MAX_SNAKE_LEN-2));
end if;
else
-- add one at the end
if dir_right then
-- moving right, add on left
data := std_logic_vector(unsigned(data) or shift_left(unsigned(data), 1));
else
-- moving left, add on right
data := std_logic_vector(unsigned(data) or shift_right(unsigned(data), 1));
end if;
end if;
snake <= data;
end if;
end process;
-- move snake on clock
process(clk)
-- variables in the process
variable data : std_logic_vector(LEN-1 downto 0);
begin
-- shift the snake
if rising_edge(clk) then
data := snake;
if dir_right then
-- right move
data(LEN-2 downto 0) := data(LEN-1 downto 1);
if data(0) = '1' then
dir_right <= FALSE; -- change direction
end if;
else
-- left move
data(LEN-1 downto 1) := data(LEN-2 downto 0);
if data(LEN-1) = '1' then
dir_right <= TRUE; -- change direction
end if;
end if;
snake <= data;
end if;
end process;
-- send changed data to output
output <= snake;
end Anaconda;
How fast is your clock? Typically the clock in an FPGA is pretty fast (50MHz or higher). If it is fast, you will often sample your "toggle" signal on each clock edge with a delay element to detect edges. You can use this edge detect to increase the length of the snake. If you need to move the snake slowly enough for a human to detect it, you will typically only move a step every x number of clock edges (using a counter to generate a clock enable). This can all be done in a single process that is only reliant on your high speed clock edge.
If your toggle signal is sourced from a physical switch, however, you may want to be careful about about the physical phenomenon of switch bouncing. You can avoid most bouncing issues by ignoring positive edges for a certain number of counts after detecting a negative edge. This way only the first positive edge counts when you press the switch and none of the (bouncing) positive edges are used on releasing the switch.
If any of what I said above doesn't make sense I can go into more detail.
Here is an example of what I said above with a snake that always rotates right, begins 2 bits wide, and has no limit on the maximum size (warning: not tested):
architecture behav of snake is
signal clk : std_logic; --actually an input. 50MHz
signal toggle : std_logic := '0'; --actually an input, not an internal signal
signal toggle_d : std_logic := '0';
signal snake_vec : std_logic_vector(17 downto 0) := "000000000000000011";
signal disable_count : unsigned(20 downto 0):=(others => '0'); --about 50ms to cycle through full range
signal step_count : unsigned(24 downto 0):=(others => '0'); --about 0.7s to cycle through full range
begin
process(clk)
if (clk = '1' and clk'event) then
toggle_d <= toggle; --store previous value of toggle
if (toggle_d = '1' and toggle = '0') then --trigger blocking counter on negative edges of toggle
disable_count <= (others => '1');
elsif (disable_count /= 0) then --count down if blocking counter is enabled
disable_count <= disable_count-1;
end if;
if (toggle_d = '0' and toggle = '1' and disable_count = 0) then --extend on allowed rising edges of toggle
snake_vec <= snake_vec or (snake_vec(0) & snake_vec(17 downto 1); --extend the snake by 1
else
step_count <= step_count-1; --this could be + or -
if (step_count = 0) then --functions as a clock enable every 1 in 33.5 million cycles
snake_vec <= snake_vec(0) & snake_vec(17 downto 0); --rotate snake
end if;
end if;
end if;
end process;
end behav;
EDIT: with the situation you described in the comments, the best way would probably be to latch the event on the high speed clock and read it with the low speed clock. Below is an example how to do that. Note that your clock divider needs to give an output on the high speed clock for each rising edge of the divided clock (assuming a counter-based clock divider). For your project, you may want to do the high speed latching outside of the block that uses the slow speed clock - it can pass the event as an input.
architecture behav of snake is
signal clk : std_logic; --50MHz
signal divide_event : std_logic; --on the 50MHz domain, single clock wide pulse every rising edge of 4Hz clock, sourced from clock divider
signal clk_slow : std_logic; --4Hz
signal toggle : std_logic := '0'; --actually an input, not an internal signal
signal toggle_d : std_logic := '0';
signal snake_vec : std_logic_vector(17 downto 0) := "000000000000000011";
begin
process(clk)
if (clk = '1' and clk'event) then
toggle_d <= toggle; --store previous value of toggle
if (toggle_d = '0' and toggle = '1') then
extend_event <= '1';
elsif divide_event = '1' then
extend_event <= '0';
end if;
end process;
process(clk_slow)
if (clk_slow = '1' and clk_slow'event) then
if (extend_event = '1') then
snake_vec <= snake_vec or (snake_vec(0) & snake_vec(17 downto 1); --extend the snake by 1
else
snake_vec <= snake_vec(0) & snake_vec(17 downto 0); --rotate snake
end if;
end if;
end process
end behav;