-- Copyright (c) 1993,1994 by Exemplar Logic, Inc. All Rights Reserved.
--
-- This source file may be used and distributed without restriction
-- provided that this copyright statement is not removed from the file
-- and that any derivative work contains this copyright notice.
--
-----------
--
-- This is a synthesizable description that implements an emulator
-- of the Mancala game (African beans game).
--
-- Description of the Hardware
-------------------------------
--
-- The hardware for the game includes a number of displays, each with a button and
-- a light, that each represent a 'bin' that can store marbles (beans).
--
-- The display indicates the number of marbles in each bin at any given time.
-- The light indecates that the present bin is not empty and that pushing the
-- button is a valid move in the game.
--
-- The button for each display indicates that a player takes the marbles from
-- the selected bin, and takes them in his hand. The hand is represented by a
-- diplay itself (no button).
--
-- Each player has a home bin, located on opposite sides of the game. The home
-- bin is also represented by a display. There should not be a button on the
-- home bins, since the game does not allow the removal of marbles from the home
-- bins.
--
-- Besides this, the game has a button to start the game, and a reset for power-up
-- purposes.
--
-- Here is a picture that represents the hardware setup of the game :
--
--
-- * == Light for valid move or to indicate the player who is active
-- O == Button to make move
-- _
-- | |
-- - == 7 - segment display
-- |_|
--
-- work bins
-- * O * O * O * O
-- _ _ _ _ _ _ _ _
-- | | | | | | | | | | | | | | | |
-- - - - - - - - -
-- * |_| |_| |_| |_| |_| |_| |_| |_| *
-- _ _ _ _
-- | | | | | | | |
-- - - - -
-- |_| |_| |_| |_|
--
-- home bin LEFT home bin right
-- * O * O * O * O
-- _ _ _ _ _ _ _ _
-- | | | | | | | | | | | | | | | |
-- - - - - - - - -
-- |_| |_| |_| |_| |_| |_| |_| |_|
--
-- work bins
--
-- _ _
-- | | | |
-- - - O Start Game
-- |_| |_|
--
-- Hand bin
--
--
-- The Rules of the game
------------------------
--
-- At the start of the game, the left player is active and can make a move.
-- The left player selects a bin (by pressing the corresponding button).
-- The machine will move the marbles from the bin (display) to the hand (diplay)
-- and drop one marble in each successive bin (clockwise) from the hand,
-- starting with the bin clock-wise adjecent to the selected bin.
-- A marble is never dropped in a opponents home bin (will be skipped).
--
-- If the last marble from the hand is dropped in an empty bin, the players
-- switch turns, and it is the other players turn to make a move.
--
-- If the last marble from the hand is dropped in the players home bin,
-- the player can make another move.
--
-- If the last marble from the hand is dropped in a non-empty work bin,
-- all the marbles from that bin will be moved back to the hand and the
-- game proceeds.
--
-- The game ends if there are no more marbles in any of the work bins.
--
-- The winner of the game is the player who has most marbles in his/her
-- home bin at the end of the game.
--
--
--
-- About the design
--------------------
--
-- The design contains a controller and a data path. The controller contains
-- a state machine that defines the overall state of the game (waiting for a
-- move, end of the game, playing).
-- The controller also has a register that defines which bin is active at any
-- point in time during active playing.
--
-- The controller provides signals for the data path to decrement the hand
-- marble count, or load the hand with the selected work bin count, or indecate
-- that the game is over and a winner should be defined etc.
--
-- The data path contains a register for each bin in the game.
-- The number of bins is easily programmable by setting a integer constant.
-- The data path also contains counters to decrement the hand marble count
-- or increment the bin marble counts.
--
-- The data path provides signals for the controller to indicate that the
-- hand bin is empty, or which of the work bins is empty.
--
-- The work bin registers are loaded with a equal number of marbles at the start
-- of the game. The total number of marbles in the game is programmable by setting
-- a generic in the top entity.
--
-- The data path also includes light drivers for the lights on each button that
-- indicate a valid move, and the lights that indicate which player is active.
-- Two extra signals are generated by the data path that let the home bin
-- display of the winner of the game blink on and off (at the end of the game).
--
-- The design does not include a merry-go-round display driver. This is done
-- outside this design, on the Aptix board.
--
-- The design does also not include a 18 bit clock devider that provides a
-- vary slow ticking clock to let humans follow the moves of the machine
-- cycle by cycle.
--
--
library ieee ;
use ieee.std_logic_1164.all ;
package mancala_pack is
type boolean_array is array (natural range <>) of boolean ;
type player_t is (LEFT, RIGHT, BOTH, NEITHER) ;
-- Define the number of bins in the game here.
-- This include the two home bins
constant nr_of_bins : natural := 10 ;
-- Define the indexes of the two home bins
constant OUTER_LEFT : natural := 0 ;
constant OUTER_RIGHT : natural := nr_of_bins/2 ;
-- Make a 'mask' constant that eliminates the home bins
constant not_home_bins : boolean_array (nr_of_bins-1 downto 0) :=
(OUTER_LEFT=>FALSE, OUTER_RIGHT=>FALSE, OTHERS=>TRUE) ;
-- Component Declaration of the controller of the game
component control
generic (nr_of_bins : natural := 32) ;
port (start_game : in boolean ;
reset, clk : in std_logic ;
buttons : in boolean_array (nr_of_bins-1 downto 0) ;
empty_bins : in boolean_array (nr_of_bins-1 downto 0) ;
hand_is_empty : in boolean ;
active_bin : buffer boolean_array (nr_of_bins-1 downto 0) ;
decrement_hand : out boolean ;
load_hand_with_active_bin : out boolean ;
the_player : out player_t ;
end_of_the_game : out boolean ;
waiting_for_move : out boolean
) ;
end component ;
end mancala_pack ;
library ieee ;
use ieee.std_logic_1164.all ;
use work.mancala_pack.all ;
entity control is
generic (nr_of_bins : natural := 10) ;
port (start_game : in boolean ;
reset, clk : in std_logic ;
buttons : in boolean_array (nr_of_bins-1 downto 0) ;
empty_bins : in boolean_array (nr_of_bins-1 downto 0) ;
hand_is_empty : in boolean ;
active_bin : buffer boolean_array (nr_of_bins-1 downto 0) ;
decrement_hand : out boolean ;
load_hand_with_active_bin : out boolean ;
the_player : out player_t ;
end_of_the_game : out boolean ;
waiting_for_move : out boolean
) ;
end control ;
architecture exemplar of control is
type state_t is (PLAY, WAIT_FOR_MOVE, END_OF_GAME);
-- The state variables for the controller state machine
signal present_state, next_state : state_t ;
-- A separate register (one-hot) defines which bin is active
signal present_active_bin : boolean_array(nr_of_bins-1 downto 0) ;
signal player : player_t ;
signal switch_player : boolean ;
signal last_bin_was_empty, next_bin_is_empty : boolean ;
-- Shift routine to shift to the next bin.
function shift(sel : boolean_array) return boolean_array is
begin
-- shift this register to the right, roll over right bit to left
return sel(sel'right) & sel(sel'left downto sel'right+1);
end ;
-- General routine to check if a boolean array contains all 'false' elements.
function is_empty (bins : boolean_array) return boolean is
constant empty : boolean_array (bins'range) := (others=>false) ;
begin
return (bins = empty) ;
end ;
begin
process (clk, reset)
begin
if (reset='1') then
present_state <= END_OF_GAME ;
last_bin_was_empty <= FALSE ;
present_active_bin <= (others=>false) ;
elsif (clk'event and clk='1') then
present_state <= next_state ;
last_bin_was_empty <= next_bin_is_empty ;
present_active_bin <= active_bin ;
end if ;
end process ;
process (start_game,present_state,hand_is_empty,empty_bins,buttons,
present_active_bin, last_bin_was_empty, player)
variable next_active_bin : boolean_array (present_active_bin'range) ;
begin
load_hand_with_active_bin <= FALSE ;
decrement_hand <= FALSE ;
switch_player <= FALSE ;
waiting_for_move <= FALSE ;
next_bin_is_empty <= FALSE ;
end_of_the_game <= FALSE ;
case present_state is
when PLAY =>
if (hand_is_empty) then
-- No more marbles in the hand.
if (is_empty (present_active_bin AND not_home_bins)) then
-- Stop if we drop the last marble in our own bin
next_state <= WAIT_FOR_MOVE ;
active_bin <= (others=>false) ;
elsif (last_bin_was_empty) then
-- Stop and switch players if we drop the last marble
-- in an empty bin
switch_player <= TRUE ;
next_state <= WAIT_FOR_MOVE ;
active_bin <= (others=>false) ;
else
-- Continue if last marble dropped in a non-empty bin.
-- Re-load hand with the full bin contents.
next_state <= PLAY ;
active_bin <= present_active_bin ;
load_hand_with_active_bin <= TRUE ;
end if ;
else
-- Regular state to go to next bin during play
next_state <= PLAY ;
decrement_hand <= TRUE ;
-- go to the next bin
next_active_bin := shift(present_active_bin) ;
-- We dont have to drop a marble in the opponents bin :
-- shift one bin further if this is about to happen
if ((player=LEFT and next_active_bin(OUTER_RIGHT)) OR
(player=RIGHT and next_active_bin(OUTER_LEFT))) then
next_active_bin := shift (next_active_bin) ;
end if ;
-- If the bin we go to is empty, flag that now, since
-- we need to do something different in the next cycle.
if (NOT is_empty (next_active_bin AND empty_bins)) then
next_bin_is_empty <= TRUE ;
end if ;
active_bin <= next_active_bin ;
end if ;
when WAIT_FOR_MOVE =>
waiting_for_move <= TRUE ;
if (is_empty((NOT empty_bins) AND not_home_bins)) then
-- Here, there are no more marbles in any of the
-- play bins. This is the end of the game.
next_state <= END_OF_GAME ;
active_bin <= (others=>FALSE) ;
elsif (is_empty(buttons AND (NOT empty_bins) AND not_home_bins)) then
-- Here, No button was pushed that is valid (not
-- selecting a empty bin and not selecting a home bin.
next_state <= WAIT_FOR_MOVE ;
active_bin <= (others=>FALSE) ;
else
-- Somebody pushed a button. Load the hand with selected
-- bin and restart the play
next_state <= PLAY ;
load_hand_with_active_bin <= TRUE ;
active_bin <= buttons ; -- Should have only ONE bit set
end if ;
when END_OF_GAME =>
-- Let the datapath calculate who the winner is
end_of_the_game <= TRUE ;
active_bin <= (others=>false) ;
if (start_game) then
next_state <= WAIT_FOR_MOVE ;
else
next_state <= END_OF_GAME ;
end if ;
end case ;
end process ;
--
-- Process that controls which player is on.
-- The state machine defines when players have to be switched
--
process (clk, reset)
procedure switch_players (signal pl : inout player_t) is
begin
if (pl=LEFT) then
pl <= RIGHT ;
elsif (pl=RIGHT) then
pl <= LEFT ;
end if ;
end ;
begin
if (reset='1') then
player <= NEITHER ;
elsif (clk'event and clk='1') then
if (start_game) then
player <= LEFT ;
else
if (switch_player) then
switch_players (player) ;
end if ;
end if ;
end if ;
end process ;
the_player <= player ;
end exemplar ;
library ieee ;
use ieee.std_logic_1164.all ;
use work.mancala_pack.all ;
entity mancala is
generic (max_marbles : natural := 32) ;
port (start_game : boolean ;
active_bin_value : buffer integer range 0 to max_marbles-1;
active_bin : buffer boolean_array(nr_of_bins-1 downto 0);
blink_right, blink_left : inout std_logic;
clk, reset : in std_logic;
buttons : in boolean_array(nr_of_bins-1 downto 0);
button_lights : out boolean_array(nr_of_bins-1 downto 0);
l_player, r_player : out std_logic
) ;
end mancala ;
architecture exemplar of mancala is
subtype bin_integer is integer range 0 to max_marbles-1 ;
type bin_integer_array is array (nr_of_bins-1 downto 0) of bin_integer ;
-- The bins
signal bins : bin_integer_array ;
signal incremented_bin_value : bin_integer ;
signal empty_bins : boolean_array (nr_of_bins-1 downto 0) ;
-- The hand
signal hand : bin_integer ;
signal load_hand_with_active_bin : boolean ;
signal decrement_hand : boolean ;
signal hand_is_empty : boolean ;
-- Which player is playing / winning
signal player : player_t ;
signal winner : player_t ;
signal waiting_for_move : boolean ;
signal end_of_the_game : boolean ;
begin
c : control generic map ( nr_of_bins=>nr_of_bins)
port map ( -- To controller :
start_game=>start_game,
clk=>clk,
reset=>reset,
buttons=>buttons,
empty_bins=>empty_bins,
hand_is_empty=>hand_is_empty,
-- From controller :
active_bin=>active_bin,
decrement_hand=>decrement_hand,
load_hand_with_active_bin=>load_hand_with_active_bin,
the_player=>player,
end_of_the_game=>end_of_the_game,
waiting_for_move=>waiting_for_move
) ;
--
-- Process the amount of marbles in the hand
--
process (clk, reset)
begin
if (reset='1') then
hand <= 0 ;
elsif clk'event and clk='1' then
if (start_game) then
hand <= 0 ;
elsif (load_hand_with_active_bin) then
hand <= active_bin_value ;
elsif (decrement_hand and (not hand_is_empty)) then
hand <= hand - 1 ;
end if ;
end if ;
end process ;
hand_is_empty <= (hand=0) ;
--
-- Process the amount of marbles in each bin
--
bin_procs : for i in bins'range generate
process (reset, clk)
begin
if (reset='1') then
bins(i) <= 0 ;
elsif clk'event and clk='1' then
if (start_game) then
-- Initialize the home bins to zero and the
-- work bins to a number that guarantees that there
-- will be max_marbles in the game.
if (i=OUTER_LEFT or i=OUTER_RIGHT) then
bins(i) <= 0 ;
else
bins(i) <= max_marbles/(nr_of_bins-2) ;
end if ;
elsif (active_bin(i)) then
if (load_hand_with_active_bin) then
bins(i) <= 0 ;
elsif (decrement_hand and (not hand_is_empty)) then
bins(i) <= incremented_bin_value ;
end if ;
end if ;
end if ;
end process ;
empty_bins(i) <= bins(i) = 0 ;
end generate ;
--
-- Select the bin (from the bins register) that is presently active
--
process (active_bin, bins)
begin
active_bin_value <= 0 ;
for i in bins'range loop
if (active_bin(i)) then
active_bin_value <= bins(i) ;
end if ;
end loop ;
end process ;
--
-- Calculate the incremented value of the presently selected bin
--
incremented_bin_value <= active_bin_value + 1 ;
-- Generate the light signals for the player that is active
l_player <= '1' when player=LEFT else '0' ;
r_player <= '1' when player=RIGHT else '0' ;
--
-- Define the winner
--
winner <= NEITHER when NOT end_of_the_game ELSE
BOTH when bins(OUTER_LEFT)=bins(OUTER_RIGHT) ELSE
LEFT when bins(OUTER_LEFT)>bins(OUTER_RIGHT) ELSE
RIGHT ;
--
-- Blink the display of the winner (on/off)
--
process (clk, reset)
begin
if (reset='1') then
-- Displays ON
blink_left <= '1' ;
blink_right <= '1' ;
elsif clk'event and clk='1' then
case winner is
when LEFT =>
blink_left <= NOT blink_left ;
blink_right <= '1' ;
when RIGHT =>
blink_left <= '1' ;
blink_right <= NOT blink_right ;
when BOTH =>
blink_left <= NOT blink_left ;
blink_right <= NOT blink_right ;
when OTHERS =>
blink_left <= '1' ;
blink_right <= '1' ;
end case ;
end if ;
end process ;
--
-- Button lights
-- Light on each button for possible move
--
lights : for i in button_lights'range generate
button_lights(i) <= TRUE when (waiting_for_move AND NOT empty_bins(i))
else FALSE ;
end generate ;
end exemplar ;
文章评论(0条评论)
登录后参与讨论