Yasin
Yasin

Reputation: 619

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!

Upvotes: 1

Views: 1865

Answers (2)

fru1tbat
fru1tbat

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

user1155120
user1155120

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

Addendum

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.

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

Related Questions