Search code examples
componentsvhdlsequential

How to run VHDL Components in a sequential fashion?


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)

sequential components

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!


Solution

  • 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.