\documentstyle[12pt,epsf,doublespace,fancyheadings,simplemargins]{article}

\pagestyle{fancyplain}
\lhead{Laas}
\chead{Vending Machine Controller}
\rhead{March 12, 1999}
\cfoot{}

\begin{document}

\title{Lab 2: Vending Machine Controller}
\author{Chris Laas \\ Recitation 3, TR1}
\date{Friday, March 12, 1999}
\maketitle

% Abstract
\begin{section}{Abstract}
\label{sec:abstract}

This report documents the design and implementation of a digital
device which, given appropriate sensors and actuators, could control a
coin operated vending machine.  The device, constructed out of LS
series logic, programmable logic arrays, and a static RAM module, has
all the capabilities one would expect from a standard coke machine.
In addition, it is designed to be in-the-field programmable, and is
capable of performing a self-test to check the integrity of the SRAM
chip.

\end{section}
\newpage

% Table of Contents
\pagenumbering{roman}
\cfoot{\thepage}
\tableofcontents
\newpage

% List of Figures
\listoffigures
\newpage

% Overview
\pagenumbering{arabic}
\cfoot{Page \thepage{} out of \pageref{end}}
\begin{section}{Overview}
\label{sec:overview}

The purpose of the vending machine controller is, at the most basic
level, to mediate a contract between a coke supplier, who is not
physically present during the transaction, and a customer, who is
willing to part with some coinage in return for a beverage.  To
accomplish this end, the controller accepts inputs in the form of
signals representing money dropped into the machine, and keeps an
internal record of the credit accumulated by the customer.  At some
point, the customer selects a product; the controller compares the
amount of credit against the price of the product, and if the customer
has enough credit, the product is vended and change is returned.
Otherwise, nothing happens.

From the point of view of the vending machine designer, the controller
is a digital device which accepts as inputs, signals representing
money being deposited or buttons being pressed, and returns as
outputs, signals representing the amount of credit accumulated and
whether to vend a particular product.  In addition, by sending signals
accessible only to the vendor or serviceman, the controller can be put
into modes which permit the programming and display of the prices of
the products, or into a self-test mode which verifies that the SRAM
chip is performing as expected.

Upon power-up, the controller boots into a waiting state from which it
can go, depending on the values on the switches S0 and S1, into one of
four modes: normal, programming, display, and self-test.

In normal mode, the controller behaves as any normal vending machine
would.  When the Nickel, Dime, or Quarter signals are asserted, the
credit count is incremented by 1, 2, or 5, respectively; when the Go
signal is asserted, the accumulated credit is compared against the
cost of the item selected by the S0 and S1 inputs, and if it is
sufficient, the controller subtracts the price from the credit count
and asserts the appropriate element of P[3:0].

In programming mode, the credit count is set, as before, by asserting
the Nickel, Dime, and Quarter signals.  However, when the Go signal is
asserted, the controller enters a write cycle, storing the current
count to the price of the selected product in the SRAM.

In display mode, asserting the Go signal triggers a read cycle,
setting the current count to the price of the selected product in the
SRAM.

Lastly, the self-test mode is used to check the wiring integrity and
functionality of the SRAM module.  The controller performs a total of
eight different tests in this mode:  for each of the four address
locations in the SRAM, the controller writes a pattern of all ones or
all zeros to the location, reads it back, and verifies that the
correct result is obtained.  Pulsing the Nickel input steps through
these tests one at a time, while asserting Go input causes the
controller to cycle through all eight tests and stop.  If any test
fails, the controller goes into an error mode and asserts all four
product indicators.

In any mode, the controller can be put immediately in its startup mode
by asserting the Local Reset (LReset) signal.  Asserting the Global
Reset (GReset) signal, on the other hand, causes the controller to
reset the credit count to zero, clear all prices in SRAM to zero, and
return to the initial startup state.

\end{section}
\newpage

% Description
\begin{section}{Description}
\label{sec:description}

\subsection{General Structure of the Device}

\begin{figure}
\epsfbox{circuit.eps}
\caption{Block Diagram of Circuit Layout}
\label{fig:circuit}
\end{figure}

The primary design goal for this project was to minimize physical
wiring and complexity.  Thus, the vast majority of the functionality
of the device is contained within a monolithic finite state machine,
expressed in VHDL (see section~\ref{sec:appendix2} for the full
listing) and implemented in a CPLD.  The other components of the
design are input/output devices, implemented as switches and LEDs; a
global clock, implemented with a crystal oscillator and an LS393
eight-bit counter; a synchronizing module, expressed in VHDL (see
section~\ref{sec:appendix1} for the full listing) and implemented in a
20V8 PAL; and a 6264 SRAM chip.  (See figure~\ref{fig:circuit}.)

The inputs and outputs of the total system all behave as described in
the Overview (see section~\ref{sec:overview}), with the addition of
the Power-On Reset (POR) signal, which was not in the original
specifications for the device.  The POR should be pulsed high for at
least one clock cycle, before any other signal is sent to the device;
and subsequently, it should be held low for as long as the device is
in operation.  It serves to set all flip-flops in the device to a
known state before beginning.

The simplest module of the controller is the global clock.  The two
halves of an LS393 dual four-bit counter are chained together and
clocked by the output of a 1.8 MHz crystal oscillator; the output of
this system is the most significant bit of the resulting eight-bit
counter, which oscillates as a 7.2 KHz square wave.  This output is
used as the CLK input to the synchronizing module and the main FSM.

\subsection{Synchronizer}

The synchronizer is designed to simplify the command interface to the
main FSM, as well as to align input pulses along clock edges.  The
inputs to the synchronizer come from the CLK, the POR, and the Go,
Nickel, Dime, Quarter, LReset, and GReset switches; the Button output
is a three-bit bus that indicates a command code to the main FSM.
Instead of simply stabilizing the inputs as a D register would, the
synchronizer also handles the situations that a switch is asserted and
held, or that another switch is pressed while the first is still
active.

\begin{figure}
\epsfbox{syncfsm.ps}
\caption{State Diagram of Synchronizing Module}
\label{fig:syncfsm}
\end{figure}

Implemented as a simple finite state machine (see the state diagram,
figure~\ref{fig:syncfsm}), the synchronizer's operation is as
follows.  Upon assertion of the POR, the synchronizer goes into the
{\tt wait} state, and places all zeros on the Button output.  On every
rising clock edge, the Go, Nickel, Dime, Quarter, LReset, and GReset
inputs are checked, and if one is asserted (with order of precedence
as listed), a unique code is output to Button for exactly one clock
cycle, and the synchronizer then waits for that signal to be
deasserted before recognizing any other input.  As an example, if the
synchronizer is in the {\tt wait} state and the Go input is asserted
on a rising clock edge, the FSM will enter the {\tt go1} state,
putting {\tt 100} on the Button output.  On the next rising edge of
the clock, the FSM enters the {\tt go2} state, putting {\tt 000}
(i.e. ``nothing'') on Button; it will stay in this state until the Go
input is deasserted, and then return to the {\tt wait} state.  If the
FSM is in the {\tt go2} state and, say, the Dime input goes high,
there will be no effect: it will remain in the {\tt go2} state and the
Button output will remain at {\tt 000}.

\subsection{Main FSM Overview}

\begin{figure}
\epsfbox{mainfsm.ps}
{\tiny \it Note: To conserve area, output specifications are omitted,
and complex conditions are paraphrased.}
\caption{Overview State Diagram of Main Module}
\label{fig:mainfsm}
\end{figure}

Forming the real ``brain'' of the controller, the main FSM (see the
overview state diagram, figure~\ref{fig:mainfsm}) keeps track of the
current credit; implements the various modes of operation of the
controller; outputs the appropriate values to the credit count and
product selection LEDs; and directly controls the SRAM chip.  It takes
as inputs: the CLK, the POR, the three-bit Button command code from
the synchronizer, the selection switches S0 and S1, and the value on
four bits of the SRAM data I/O bus.  As outputs, it gives: the
four-bit registered value Count, which carries the current amount of
credit accumulated; the four-bit registered vector Product, which
holds zeros until a product is vended, at which point the appropriate
line goes high (in addition, all four lines go high when the self-test
signals an error); and the write enable /WE, output enable /OE,
address lines A0 and A1, and four bits of the data bus on the SRAM.
The chip select lines /CS1 and CS2 on the SRAM are tied to GND and
PWR, respectively, and address lines two through twelve are grounded.

\begin{figure}
\epsfbox{mainfsm-startup.ps}
\caption{Startup Mode of Main FSM (detail)}
\label{fig:startup}
\end{figure}

When the POR is pulsed over a rising clock edge, the main FSM resets
to the {\tt startup} state (see figure~\ref{fig:startup}).  In this
state, the RAM is left idle, and Count and Product are not modified.
The FSM remains in this state until the Button input is Go, at which
point it chooses which mode to enter depending on the values on the
S[1:0] input:  for S equaling {\tt 00}, {\tt 01}, {\tt 10}, or {\tt
11}, the next state will be {\tt display\_wait}, {\tt
programming\_wait}, {\tt normal\_wait}, or {\tt selftest\_wait},
respectively.

\subsection{Display Mode}

\begin{figure}
\epsfbox{mainfsm-display.ps}
\caption{Display Mode of Main FSM (detail)}
\label{fig:display}
\end{figure}

In Display mode (see figure~\ref{fig:display}), the controller idles
in the {\tt display\_wait} state until the Go button is pressed.  In
this mode, the address on the SRAM comes from the S[1:0] input, as
each address corresponds to one of the four products.  When the Go
button is pressed, the controller performs a read cycle on the SRAM:
it first pulls /OE low, and on the next clock cycle, reads the value
on the I/O bus into the Count register (which is displayed on the Hex
LEDs).  Pressing the LReset button while in the {\tt display\_wait}
state puts the FSM back into the {\tt startup} state; pressing the
GReset button clears Count and sends the FSM into the state {\tt
greset1}, which will be discussed later.

\subsection{Programming Mode}

\begin{figure}
\epsfbox{mainfsm-programming.ps}
\caption{Programming Mode of Main FSM (detail)}
\label{fig:programming}
\end{figure}

In Programming mode (see figure~\ref{fig:programming}), the controller
idles in the {\tt programming\_wait} state.  If any of the Nickel,
Dime, or Quarter buttons is pressed, the FSM remains in the {\tt
programming\_wait} state, but Count is incremented either by 1, by 2,
or by 5, depending on the button.  As in the Display mode, the SRAM
address comes from S[1:0].  If the Go button is signaled, the FSM
executes a write cycle to the SRAM, by asserting the value of Count on
the I/O bus and pulsing /WE low for one clock cycle.  Upon returning
to the {\tt programming\_wait} state from the write cycle, Count is
cleared.  The {\tt programming\_wait} state reacts to the LReset and
GReset signals as the {\tt display\_wait} state does:  LReset sets the
state to {\tt startup}, and GReset sets the state to {\tt greset1},
clearing Count.

\subsection{Normal Mode}

\begin{figure}
\epsfbox{mainfsm-normal.ps}
\caption{Normal Mode of Main FSM (detail)}
\label{fig:normal}
\end{figure}

Normal mode is much like Display and Programming modes, but slightly
more complex.  Idling in the {\tt normal\_wait} state, the Nickel,
Dime, and Quarter buttons have the same effect as in Programming mode.
As before, the A[1:0] output comes directly from S[1:0].  When the Go
button is pressed, a read cycle is initiated on the SRAM: the {\tt
normal\_go1} state sets /OE low, and the {\tt normal\_go2} state reads
the value on the I/O bus.  If the value retrieved from RAM is greater
than Count, then {\tt normal\_go2} returns to {\tt normal\_wait}
without modifying the state further.  However, if this value is less
than or equal to Count, Count is decremented by the value, Product
gets $2^S$, which asserts the appropriate product signal selected by
S[1:0], and the state returns to {\tt normal\_wait}.  The state {\tt
normal\_wait} has the standard response to the LReset and GReset codes
on Button[2:0].

\subsection{Self-Test Mode}

\begin{figure}
\epsfbox{mainfsm-selftest.ps}
\caption{Self-Test Mode of Main FSM (detail)}
\label{fig:selftest}
\end{figure}

Self-test mode is, interestingly, the most complex mode of the FSM.
Upon entering this mode, the FSM goes into state {\tt selftest\_wait}
and idles until a button is pressed.  If either of Go or Nickel is
pressed, the FSM enters state {\tt selftest\_cycle1} and Count is
reset to a starting value; however, the high bit of Count is set
differently depending on which button was pressed, to indicate whether
the tests should be stepped through (Nickel, Count(3) = '0') one at a
time or cycled through (Go, Count(3) = '1') automatically.  Either
way, the sequence of states {\tt selftest\_cycle1} through {\tt
selftest\_cycle5} writes, to address Count[1:0], a pattern of all
zeros ({\tt 0000}) if Count(2) = '0', or a pattern of all ones ({\tt
1111}) if Count(2) = '1'.  It then reads the value at that location
back and, in the exit from state {\tt selftest\_cycle5}, tests whether
the data bus holds the expected value.  If not, the FSM enters state
{\tt selftest\_error}, which asserts all bits of Product[3:0] and
waits until LReset or GReset is signalled.\

If the test succeeds, the FSM goes into state {\tt selftest\_cycle6},
which checks Count(3) to determine whether the self-test is in
step-through or cycle mode.  If it is in step-through mode, it drops
back into {\tt selftest\_wait}.  If it is in cycle mode, it either
goes into {\tt selftest\_wait} or {\tt selftest\_cycle1}, depending on
whether Count[2:0] = {\tt 111} or not, since Count[2:0] = {\tt 111}
means that all addresses and data values have been cycled through.  In
all cases, Count is incremented upon leaving the state {\tt
selftest\_cycle6}, so that on the next cycle, the next address and
data combination is tested.

Since both {\tt selftest\_wait} and {\tt selftest\_error} are
``waiting'' states, in that the FSM remains in them for more than one
clock cycle at a time, they both handle the standard LReset and GReset
semantics:  LReset sends the FSM into state {\tt startup}, and GReset
clears Count and sends the FSM into state {\tt greset1}, which will
now be explained.

\begin{figure}
\epsfbox{selftest-trace.ps}
\caption{Timing Diagram of the Self-Test Function}
\label{fig:sttrace}
\end{figure}

Figure~\ref{fig:sttrace} shows a timing diagram of the self-test
function executing in cycle mode.  At time (A), signals S0 and S1 are
held high while a pattern of {\tt 100}, corresponding to the Go
signal, is pulsed on Button[2:0]; this switches the FSM into self-test
mode.  At time (B), the Go pattern is again pulsed on Button[2:0],
causing the FSM to run through a self-test cycle.  Time (C) is a write
cycle: IO[3:0] is held low and addr[1:0] is set to zero as /WE is
pulsed low.  Likewise, time (D) is a read cycle: addr[1:0] has been
set to zero and /OE has been held low since the previous clock cycle,
and at time (D), the value on IO[3:0] is read by the FSM.

\subsection{Global Reset Function}

\begin{figure}
\epsfbox{mainfsm-greset.ps}
\caption{Global Reset Function of Main FSM (detail)}
\label{fig:greset}
\end{figure}

The Global Reset is a complex operation which must be performed upon
exiting any mode by means of the GReset button.  Thus, it has a
separate subsystem within the main FSM, consisting of the four states
{\tt greset1}, {\tt greset2}, {\tt greset3}, and {\tt greset4}.

In operation, the global reset is similar to a restricted, simplified
form of the self-test.  Instead of writing and reading every
combination of values, the global reset merely writes zeros to every
address, and does not check the integrity of the result.

When state {\tt greset1} is initially entered due to a GReset signal,
the value of Count is expected to be zero.  A single cycle of the
state cycle {\tt greset1, greset2, greset3, greset4, greset1...} puts
Count[1:0] on the address lines A[1:0] and zeros on the data bus, then
pulses /WE low, brings /WE back high, and tests whether Count[1:0] is
equal to {\tt 11}.  If it is, all addresses have been cleared, and so
Product and Count are set to zero and the FSM goes into state {\tt
startup}.  If Count[1:0] is not {\tt 11}, Count is incremented and the
process is repeated by entering state {\tt greset1}.

\end{section}
\newpage

% Conclusion
\begin{section}{Conclusion}
\label{sec:conclusion}

This design suffers from two flaws.  One is that placing more than 75
cents of credit into the controller will overflow the counter.  This
could be fixed by adding comparators to ensure that change is returned
if the counter exceeds 75 cents or by increasing the size of the
counter.  Neither of these were practical solutions in this case,
however, because the design barely fit into the CPLD provided, and
neither of those measures would fit.  The second flaw is that, since
the mechanism for returning change was not well specified in the
assignment, it is vague in the implementation.  The controller assumes
that there exists an external mechanism by which when a product is
vended, the amount of credit remaining (after the price of the product
is subtracted) is returned as change.

A fairly clever feature of this controller is the synchonizer.  It
saved much debugging effort to have an external chip perform the
functions of synchonizing and mediating among several command signals,
instead of integrating that functionality into the main FSM.  It also
saved a small amount of wiring effort to collapse the six external
inputs into a three-wire bus.

Aside from the synchronizers, this design is not a particularly
innovative one, nor is it particularly clever.  However, the most
interesting thing about it is the fact that due to being extremely
simple and well-tested under simulation, it worked correctly the first
time it was wired together.  A logic analyzer never had to be
connected to the system to debug the hardware.  This is a very
important result, because in a real-world design problem, the time
spent debugging ``clever hacks'' or poorly-thought-out shortcuts often
dwarfs any potential gain from the shortcut.  Especially in a simple
project like this one, the straightforward approach is probably the
best.

To wrap up: with some slight modifications, a design very similar to
this one could be used in a real-world situation requiring an
electronic mediator between an absent seller and coin-wielding
customer, and probably has, at some point or another.  The challenges,
though non-trivial, are also very surmountable.

\end{section}
\newpage

% Appendices
\begin{section}{Appendix I:  VHDL code for synchronizing FSM}
\label{sec:appendix1}

{\small \begin{verbatim}
library ieee;
use ieee.std_logic_1164.all;

entity lab2sync is port (
    clk, reset, go, nickel, dime, quarter, lreset, greset : in std_logic;
    button : out std_logic_vector(2 downto 0));
end lab2sync;

architecture fsm of lab2sync is
    type states is (waiting, go1, go2, nickel1, nickel2, dime1, dime2,
                    quarter1, quarter2, lreset1, lreset2, greset1, greset2);
    signal state : states;
    constant      no_button : std_logic_vector(2 downto 0) := "000";
    constant      go_button : std_logic_vector(2 downto 0) := "100";
    constant  nickel_button : std_logic_vector(2 downto 0) := "001";
    constant    dime_button : std_logic_vector(2 downto 0) := "010";
    constant quarter_button : std_logic_vector(2 downto 0) := "011";
    constant  lreset_button : std_logic_vector(2 downto 0) := "110";
    constant  greset_button : std_logic_vector(2 downto 0) := "111";
begin
    process(clk, reset, button)
    begin
        if rising_edge(clk) then
            if reset = '1' then
                state <= waiting;
            else
                case state is
                    when waiting =>  button <= no_button;
                        if go = '1' then
                            state <= go1;
                        elsif nickel = '1' then
                            state <= nickel1;
                        elsif dime = '1' then
                            state <= dime1;
                        elsif quarter = '1' then
                            state <= quarter1;
                        elsif lreset = '1' then
                            state <= lreset1;
                        elsif greset = '1' then
                            state <= greset1;
                        else
                            state <= waiting;
                        end if;
                    when go1 =>      button <= go_button;
                        state <= go2;
                    when go2 =>      button <= no_button;
                        if go = '1' then
                            state <= go2;
                        else
                            state <= waiting;
                        end if;
                    when nickel1 =>  button <= nickel_button;
                        state <= nickel2;
                    when nickel2 =>  button <= no_button;
                        if nickel = '1' then
                            state <= nickel2;
                        else
                            state <= waiting;
                        end if;
                    when dime1 =>    button <= dime_button;
                        state <= dime2;
                    when dime2 =>    button <= no_button;
                        if dime = '1' then
                            state <= dime2;
                        else
                            state <= waiting;
                        end if;
                    when quarter1 => button <= quarter_button;
                        state <= quarter2;
                    when quarter2 => button <= no_button;
                        if quarter = '1' then
                            state <= quarter2;
                        else
                            state <= waiting;
                        end if;
                    when lreset1 =>  button <= lreset_button;
                        state <= lreset2;
                    when lreset2 =>  button <= no_button;
                        if lreset = '1' then
                            state <= lreset2;
                        else
                            state <= waiting;
                        end if;
                    when greset1 =>  button <= greset_button;
                        state <= greset2;
                    when greset2 =>  button <= no_button;
                        if greset = '1' then
                            state <= greset2;
                        else
                            state <= waiting;
                        end if;
                    when others =>   button <= no_button;
                        state <= waiting;
                end case;
            end if;
        end if;
    end process;
end fsm;
\end{verbatim}}

\end{section}
\newpage

\begin{section}{Appendix II:  VHDL code for main FSM}
\label{sec:appendix2}

{\small \begin{verbatim}
library ieee;
use ieee.std_logic_1164.all;
use work.numeric_std.all;

entity lab2main is port (
    clk, reset : in std_logic;
    s          : in std_logic_vector(1 downto 0);
    button     : in std_logic_vector(2 downto 0);
    io         : inout unsigned(3 downto 0);--
    count      : buffer unsigned(3 downto 0);--
    product    : out std_logic_vector(3 downto 0);
    addr       : out std_logic_vector(1 downto 0);
    n_we, n_oe : out std_logic);
end lab2main;

architecture fsm of lab2main is
    type states is (startup,
                    normal_wait, normal_go1, normal_go2,
                    programming_wait, programming_go1, programming_go2,
                    display_wait, display_go1, display_go2,
                    selftest_wait, selftest_cycle1, selftest_cycle2,
                    selftest_cycle3, selftest_cycle4, selftest_cycle5,
                    selftest_cycle6, selftest_error,
                    greset1, greset2, greset3, greset4);
    signal state : states;

    signal count_p_1, count_p_2, count_p_5 : unsigned(3 downto 0);--

    signal io_z : std_logic;
    signal io_data : unsigned(3 downto 0);--

    constant      no_button : std_logic_vector(2 downto 0) := "000";
    constant      go_button : std_logic_vector(2 downto 0) := "100";
    constant  nickel_button : std_logic_vector(2 downto 0) := "001";
    constant    dime_button : std_logic_vector(2 downto 0) := "010";
    constant quarter_button : std_logic_vector(2 downto 0) := "011";
    constant  lreset_button : std_logic_vector(2 downto 0) := "110";
    constant  greset_button : std_logic_vector(2 downto 0) := "111";
begin
    io <= (others=>'Z') when io_z = '1' else io_data;

    count_p_1 <= count + 1;
    count_p_2 <= count + 2;
    count_p_5 <= count + 5;

    process(clk, reset, state, count, product, s, button, io)
    begin
        if rising_edge(clk) then
            if reset = '1' then
                addr <= "00";
                n_we <= '1'; n_oe <= '1';
                io_z <= '1'; io_data <= (others=>'0');

                state   <= startup;
                count   <= (others=>'0');
                product <= "0000";
            else
                case state is
                    when startup =>
                        addr <= "00";
                        n_we <= '1'; n_oe <= '1';
                        io_z <= '1'; io_data <= (others=>'0');
                        product <= "0000";
                        if button = go_button then
                            case s is
                                when "00"   => state <= display_wait;
                                when "01"   => state <= programming_wait;
                                when "10"   => state <= normal_wait;
                                when "11"   => state <= selftest_wait;
                                when others => state <= startup;
                            end case;
                        else
                            state <= startup;
                        end if;

                    when display_wait =>
                        addr <= s;
                        n_we <= '1'; n_oe <= '1';
                        io_z <= '1'; io_data <= (others=>'0');
                        case button is
                            when go_button =>
                                state <= display_go1;
                            when lreset_button =>
                                state <= startup;
                            when greset_button =>
                                count <= (others=>'0');
                                state <= greset1;
                            when others =>
                                state <= display_wait;
                        end case;
                    when display_go1 =>
                        addr <= s;
                        n_we <= '1'; n_oe <= '0';
                        io_z <= '1'; io_data <= (others=>'0');
                        state <= display_go2;
                    when display_go2 =>
                        addr <= s;
                        n_we <= '1'; n_oe <= '0';
                        io_z <= '1'; io_data <= (others=>'0');
                        count <= io;
                        state <= display_wait;

                    when programming_wait =>
                        addr <= s;
                        n_we <= '1'; n_oe <= '1';
                        io_z <= '0'; io_data <= count;
                        case button is
                            when go_button =>
                                state <= programming_go1;
                            when nickel_button =>
                                count <= count_p_1;
                                state <= programming_wait;
                            when dime_button =>
                                count <= count_p_2;
                                state <= programming_wait;
                            when quarter_button =>
                                count <= count_p_5;
                                state <= programming_wait;
                            when lreset_button =>
                                state <= startup;
                            when greset_button =>
                                count <= (others=>'0');
                                state <= greset1;
                            when others =>
                                state <= programming_wait;
                        end case;
                    when programming_go1 =>
                        addr <= s;
                        n_we <= '0'; n_oe <= '1';
                        io_z <= '0'; io_data <= count;
                        state <= programming_go2;
                    when programming_go2 =>
                        addr <= s;
                        n_we <= '1'; n_oe <= '1';
                        io_z <= '0'; io_data <= count;
                        count <= (others=>'0');
                        state <= programming_wait;

                    when normal_wait =>
                        addr <= s;
                        n_we <= '1'; n_oe <= '1';
                        io_z <= '1'; io_data <= (others=>'0');
                        case button is
                            when go_button =>
                                state <= normal_go1;
                            when nickel_button =>
                                count <= count_p_1;
                                state <= normal_wait;
                            when dime_button =>
                                count <= count_p_2;
                                state <= normal_wait;
                            when quarter_button =>
                                count <= count_p_5;
                                state <= normal_wait;
                            when lreset_button =>
                                state <= startup;
                            when greset_button =>
                                count <= (others=>'0');
                                state <= greset1;
                            when others =>
                                state <= normal_wait;
                        end case;
                    when normal_go1 =>
                        addr <= s;
                        n_we <= '1'; n_oe <= '0';
                        io_z <= '1'; io_data <= (others=>'0');
                        state <= normal_go2;
                    when normal_go2 =>
                        addr <= s;
                        n_we <= '1'; n_oe <= '0';
                        io_z <= '1'; io_data <= (others=>'0');
                        if count >= io then
                            case s is
                                when "00"   => product <= "0001";
                                when "01"   => product <= "0010";
                                when "10"   => product <= "0100";
                                when "11"   => product <= "1000";
                                when others => product <= "----";
                            end case;
                            count <= count - io;
                            state <= normal_wait;
                        else
                            state <= normal_wait;
                        end if;

                    when selftest_wait =>
                        addr <= "00";
                        n_we <= '1'; n_oe <= '1';
                        io_z <= '1'; io_data <= (others=>'0');
                        case button is
                            when go_button =>
                                count <= (3=>'1', others=>'0');
                                state <= selftest_cycle1;
                            when nickel_button =>
                                count(3) <= '0';
                                state <= selftest_cycle1;
                            when lreset_button =>
                                state <= startup;
                            when greset_button =>
                                count <= (others=>'0');
                                state <= greset1;
                            when others =>
                                state <= selftest_wait;
                        end case;
                    when selftest_cycle1 =>
                        addr <= std_logic_vector(count(1)&count(0));
                        n_we <= '1'; n_oe <= '1';
                        io_z <= '0';
                        io_data <= (0 to 3 => count(2));
                        state <= selftest_cycle2;
                    when selftest_cycle2 =>
                        addr <= std_logic_vector(count(1)&count(0));
                        n_we <= '0'; n_oe <= '1';
                        io_z <= '0';
                        io_data <= (0 to 3 => count(2));
                        state <= selftest_cycle3;
                    when selftest_cycle3 =>
                        addr <= std_logic_vector(count(1)&count(0));
                        n_we <= '1'; n_oe <= '1';
                        io_z <= '0';
                        io_data <= (0 to 3 => count(2));
                        state <= selftest_cycle4;
                    when selftest_cycle4 =>
                        addr <= std_logic_vector(count(1)&count(0));
                        n_we <= '1'; n_oe <= '0';
                        io_z <= '1'; io_data <= (others=>'0');
                        state <= selftest_cycle5;
                    when selftest_cycle5 =>
                        addr <= std_logic_vector(count(1)&count(0));
                        n_we <= '1'; n_oe <= '0';
                        io_z <= '1'; io_data <= (others=>'0');
                        if io(0) = count(2) and io(1) = count(2)
                            and io(2) = count(2) and io(3) = count(2) then
                            state <= selftest_cycle6;
                        else
                            state <= selftest_error;
                        end if;
                    when selftest_cycle6 =>
                        addr <= std_logic_vector(count(1)&count(0));
                        n_we <= '1'; n_oe <= '1';
                        io_z <= '1'; io_data <= (others=>'0');
                        count <= count_p_1;
                        if count(3) = '1' and
                            (count(2) = '0'
                             or count(1) = '0'
                             or count(0) = '0') then
                            state <= selftest_cycle1;
                        else
                            state <= selftest_wait;
                        end if;
                    when selftest_error =>
                        product <= "1111";
                        case button is
                            when lreset_button =>
                                state <= startup;
                            when greset_button =>
                                count <= (others=>'0');
                                state <= greset1;
                            when others =>
                                state <= selftest_error;
                        end case;

                    when greset1 =>
                        addr <= std_logic_vector(count(1)&count(0));
                        n_we <= '1'; n_oe <= '1';
                        io_z <= '0'; io_data <= (others=>'0');
                        state <= greset2;
                    when greset2 =>
                        addr <= std_logic_vector(count(1)&count(0));
                        n_we <= '0'; n_oe <= '1';
                        io_z <= '0'; io_data <= (others=>'0');
                        state <= greset3;
                    when greset3 =>
                        addr <= std_logic_vector(count(1)&count(0));
                        n_we <= '1'; n_oe <= '1';
                        io_z <= '0'; io_data <= (others=>'0');
                        state <= greset4;
                    when greset4 =>
                        addr <= std_logic_vector(count(1)&count(0));
                        n_we <= '1'; n_oe <= '1';
                        io_z <= '0'; io_data <= (others=>'0');
                        if count(1) = '1' and count(0) = '1' then
                            product <= "0000";
                            count <= (others=>'0');
                            state <= startup;
                        else
                            count <= count_p_1;
                            state <= greset1;
                        end if;
                    when others =>
                        state <= startup;
                end case;
            end if;
        end if;
    end process;
end fsm;
\end{verbatim}}

\end{section}

\label{end}

\end{document}
