Reputation: 619
I'm making a simple TicTacToe game (computer vs user) in VHDL and the code is almost finished, but there is something that I can't figure out how to handle it.
Overall there are three main modules, computer move based on the current grid, grid status that takes in which cell to be filled either by computer or by user, a main module that retrieves the grid from the grid module and handle these components as well as the user move.
So, in this main module, I have a Component that sends the move (either by computer or user) to the grid module and in return updates its current grid Signal in this main module, then the second component (computer move) takes in the grid status (which is the Signal) and make a move based on that. Therefore, this also needs to get sent to the grid module for it to be updated. However, this doesn't really work in action when it comes to functioning in this order.
In a nutshell, my question is, how may I go about updating this main Signal in the order of the Components execution or more generally, how may I have a Signal be updated by a Component that needs to become the input of another Component respectively?
Thanks for your help.
Edit
Here is part of the main module code
ENTITY currentstate IS
PORT (to_occupy_cell : IN TO_SELECT; --user choice
start : IN STD_LOGIC; --initialize the grid
by_user : IN STD_LOGIC; --how to fill the grid (X or O)
difficulty : IN STD_LOGIC; --difficulty
winner_state : OUT INTEGER RANGE 0 to 2; --who is the winner?
currentgrid : OUT GRID (1 TO 3, 1 TO 3)); --outputs status of the grid after compmove to be displayed
END currentstate;
---------------------------------
ARCHITECTURE statearch OF currentstate IS
SIGNAL comp_occupy : TO_SELECT;
SIGNAL grid_status : GRID (1 TO 3, 1 TO 3);
COMPONENT grid IS -------saves the grid status
PORT (to_occupy_cell : IN TO_SELECT; start : IN STD_LOGIC; by_user : IN STD_LOGIC; currentgrid : OUT GRID (1 TO 3, 1 TO 3));
END COMPONENT;
COMPONENT compmove IS
PORT (current : IN GRID (1 TO 3, 1 TO 3); dif : IN STD_LOGIC; compsel : OUT TO_SELECT);
END COMPONENT;
BEGIN
U1: grid PORT MAP (to_occupy_cell, start, by_user ,grid_status);
U2: compmove PORT MAP (grid_status, difficulty, comp_occupy);
Here is the grid module
ENTITY grid IS
PORT (to_occupy_cell : IN TO_SELECT; --user choice
start : IN STD_LOGIC; --initialize the grid
by_user : IN STD_LOGIC; --how to fill the grid ( X or O)
currentgrid : OUT GRID (1 TO 3, 1 TO 3)); --outputs status of the grid after compmove to be displayed
END grid;
---------------------------------
ARCHITECTURE gridstatus OF grid IS
BEGIN
PROCESS (to_occupy_cell, start)
VARIABLE temp_grid : GRID (1 TO 3, 1 TO 3);
BEGIN
IF (start = '1' AND start'EVENT) THEN
temp_grid := (others => (others=>0));
END IF;
IF (by_user = '1') THEN
temp_grid(to_occupy_cell(0), to_occupy_cell(1)) := 1;
ELSIF (by_user = '0') THEN
temp_grid(to_occupy_cell(0), to_occupy_cell(1)) := 2;
END IF;
currentgrid <= temp_grid;
END PROCESS;
END gridstatus;
And entity of the computer move module
---------------------------------
ENTITY compmove IS
PORT(current : IN GRID (1 TO 3, 1 TO 3); --takes in the grid status
dif : IN STD_LOGIC; --difficulty
compsel : OUT TO_SELECT);
END compmove;
---------------------------------
So, what happens is that, the Signal grid_status should automatically be updated first by the user move and then when it is updated it gets piped into the compmove
module for the computer to decide which cell to fill. Afterwards, the grid_status should get updated again. Also, '1' is the user move, '2' is the computer move in the grid.
Hope this clears what I'm after.
(image referenced from link added)
EDIT 2:
Thanks to the help of David and fru1tbat I could actually get it to work! Turns out I had missed making compmove sensitive to grid_status. However, now it is only working for the first round but not the second round if the user selects another cell. Below are the updated codes:
Main module (currentstate)
ENTITY currentstate IS
PORT (to_occupy_cell : IN TO_SELECT; --user choice
start : IN STD_LOGIC; --initialize the grid
by_user : IN STD_LOGIC; --how to fill the grid ( X or O)
difficulty : IN STD_LOGIC; --difficulty
winner_state : OUT INTEGER RANGE 0 to 2); --who is the winner?
END currentstate;
---------------------------------
ARCHITECTURE statearch OF currentstate IS
SIGNAL occupy_to_grid : TO_SELECT;
SIGNAL comp_occupy : TO_SELECT;
SIGNAL user_move : STD_LOGIC;
SIGNAL grid_status : GRID (1 TO 3, 1 TO 3);
COMPONENT grid_ IS -------saves the grid status
PORT (to_occupy_cell : IN TO_SELECT; start : IN STD_LOGIC; by_user : IN STD_LOGIC; currentgrid : OUT GRID (1 TO 3, 1 TO 3));
END COMPONENT;
COMPONENT compmove IS
PORT (current : IN GRID (1 TO 3, 1 TO 3); by_user : IN STD_LOGIC; dif : IN STD_LOGIC; compsel : OUT TO_SELECT);
END COMPONENT;
BEGIN
U1: grid_ PORT MAP (occupy_to_grid, start, user_move,grid_status);
U2: compmove PORT MAP (grid_status, user_move, difficulty, comp_occupy);
PROCESS(comp_occupy, to_occupy_cell)
BEGIN
IF to_occupy_cell'EVENT THEN
occupy_to_grid <= to_occupy_cell;
user_move <= by_user;
END IF;
IF comp_occupy'EVENT THEN
occupy_to_grid <= comp_occupy;
user_move <= '0';
END IF;
END PROCESS;
END statearch;
The grid_ module
ENTITY grid_ IS
PORT (to_occupy_cell : IN TO_SELECT; --user choice
start : IN STD_LOGIC; --initialize the grid
by_user : IN STD_LOGIC; --how to fill the grid ( X or O)
currentgrid : OUT GRID (1 TO 3, 1 TO 3)); --outputs status of the grid after compmove to be displayed
END grid_;
---------------------------------
ARCHITECTURE gridstatus OF grid_ IS
BEGIN
PROCESS (to_occupy_cell, start)
VARIABLE temp_grid : GRID (1 TO 3, 1 TO 3);
BEGIN
IF (start = '1' AND start'EVENT) THEN
temp_grid := (others => (others=>0));
END IF;
IF (by_user = '1') THEN
temp_grid(to_occupy_cell(0), to_occupy_cell(1)) := 1;
ELSIF (by_user = '0') THEN
temp_grid(to_occupy_cell(0), to_occupy_cell(1)) := 2;
END IF;
currentgrid <= temp_grid;
END PROCESS;
END gridstatus;
Initial part of compmove
ENTITY compmove IS
PORT(current : IN GRID (1 TO 3, 1 TO 3); --takes in the grid status
by_user : IN STD_LOGIC; --to only gets triggered when user selects a cell
dif : IN STD_LOGIC; --difficulty
compsel : OUT TO_SELECT);
END compmove;
---------------------------------
ARCHITECTURE comparch OF compmove IS
BEGIN
PROCESS(by_user, current)
VARIABLE tempsel : TO_SELECT := (0,0);
VARIABLE occ_count : INTEGER := 0;
VARIABLE unocc : INTEGER := 0;
BEGIN
IF (dif = '0' AND current'EVENT AND by_user = '1') THEN
--------------bunch of codes here
It is almost there, I believe just a small change perhaps in the sensitivity list of compmove would do the trick for it to retrieve the updated grid_status correspondingly, because currently after the first round it doesn't take in the updated grid_status and comp_occupy remains unchanged!
Upvotes: 1
Views: 1865
Reputation: 1625
Your process that writes to the grid_status
signal only accepts input from the user (comp_occupy
doesn't connect to anything in your main module) so how can the computer update the grid with its move? What you seem to need is an input (or set of inputs) to the grid
module that connects to comp_occupy
, and a modified process in gridstatus
which monitors both players and updates the grid from either source.
(edit)
On further consideration, I realize your intent may be to have a single input to the grid
module which adds a move for either player. You appear to want to use events on by_user
to update the grid, but I don't see the logic that switches by_user
to create the sequence. You seem to need something like (in your main component):
process (to_occupy_cell, comp_occupy)
begin
if by_user = '1' then
occupy_to_grid <= to_occupy_grid;
else
occupy_to_grid <= comp_occupy;
end if;
by_user <= not by_user; -- on input from either source, alternate user for next input
end process;
Then map occupy_to_grid
to grid
instead of to_occupy_cell
. This assumes that the new value of both 'occupy' signals is assigned on the same delta (or else by_user
will flip back and forth unexpectedly), so if that's not the case, you will need to modify accordingly.
Let me know if this is close to what you're looking for.
Upvotes: 1
Reputation:
You appear to be missing by_user
from the process sensitivity list in grid:
process (to_occupy_cell, start) -- Add by_user to sensitivity list
variable temp_grid : grid (1 to 3, 1 to 3);
begin
if (start = '1' and start'event) then
temp_grid := (others => (others=>0));
end if;
if (by_user = '1') then
temp_grid(to_occupy_cell(0), to_occupy_cell(1)) := 1;
elsif (by_user = '0') then
temp_grid(to_occupy_cell(0), to_occupy_cell(1)) := 2;
end if;
currentgrid <= temp_grid;
end process;
The only thing that fit from your description was a missing event trigger the process. The idea being by_user isn't concurrent with any other input.
I also found a name conflict between grid
in the component declaration in currentstate
and the undeclared package containing the type declaration for type grid
with two different VHDL analyzers. (After making up a package and type declarations for to_select
and grid
).
I added a test bench, and also hooked up grid_status
to the returned currentgrid
in currentstate
so it would show up at the top level.
library ieee;
use ieee.std_logic_1164.all;
use work.foobat.all;
entity test is
end entity;
architecture foo of test is
component currentstate is
port (
to_occupy_cell: in to_select; --user choice
start: in std_logic; --initialize the grid
by_user: in std_logic; --how to fill the grid (x or o)
difficulty: in std_logic; --difficulty
winner_state: out integer range 0 to 2; --who is the winner?
currentgrid: out grid (1 to 3, 1 to 3)
); --outputs status of the grid after compmove to be displayed
end component;
signal to_occupy_cell: to_select;
signal start: std_logic := '0';
signal by_user: std_logic := '1';
signal difficulty: std_logic := '0';
signal winner_state: integer range 0 to 2;
signal currentgrid: grid(1 to 3, 1 to 3);
begin
DUT:
currentstate
port map (
to_occupy_cell => to_occupy_cell,
start => start,
by_user => by_user,
difficulty => difficulty,
winner_state => winner_state,
currentgrid => currentgrid -- grid_status
);
STIMULUS:
process
begin
wait for 1 sec;
to_occupy_cell <= (2,2); -- center
start <= '1'; -- write 1 to gridstatus(2,2);
wait for 1 sec;
to_occupy_cell <= (1,1); -- corner
start <= '0'; -- no separate delta cycle event
wait for 1 sec;
wait;
end process;
end architecture;
So the two sets of grid values show below are grid_status
(surfaced) and current
on the compmove
port. From what I can tell, grid_status
gets to compmove
.
This was done with ghdl and gtkwave. ghdl handles array signals in waveforms. The waveform trace image is larger than it will likely appear in your browser, you can open it in a separate tab/window or otherwise view it larger.
The values shown for the two versions grid_status
/current
appear identical.
Note there are three events on grid_status
, the initial event which writes to leftmost row and column in the grid. I'm not sure why to_occupy_cell
doesn't show up properly in the waveform, it doesn't show up as an array but does show the events. It's likely a result of how I declared the to_select
type.
The initial or default transaction, the start'event and start = '1'
transaction and to_occupy_cell transition (concurrent with start
= 0
).
Anyway, the point is that grid_status
gets to compmove
.
Upvotes: 2