これは、このような状態遷移図によって動作を表現できる回路で、
いくつかの内部状態(state)をもち、クロックにあわせて、
入力によって、次の状態が決まる、というものです。
この例では、IDLE, SET, RUN, DONEという4つの状態を持ち、
例えば、現在の状態がIDLEのときは、setという入力が0のときは
次の状態もIDLE、set=1ならば次の状態はSETとなる、という動作を
あらわしています。
もちろんカウンタなども、このステートマシンとして
考えることができます。
(内部状態=現在のカウント値、と考えればよい)
このようなステートマシンは、一般にはこのような構成の回路によって
実現することができます。
現状態stateを保持しているレジスタ(実体は、ほとんどの場合はD-FF)、
現状態と入力から次の遷移先の状態n_stateを決める次状態決定回路、
そして現状態と入力から、全体としての出力を決める
出力信号決定部、からなります。
一般には、現状態と次状態、入力・出力の対応を状態遷移表にまとめ、 それを真理値表とみなして次状態決定回路、出力信号決定部の 論理回路を設計することになります。
これから、上記のようなデータパスを設計することができます。 ここでREGは、時間カウント値を保持しておく12ビットのレジスタ(変数)で、 その実体はクロックclkに同期した12ビットのD-FFと考えてください。 このレジスタの出力はcntという名前の出力になっています。 またADDは加算回路で、ADDの下側の入力は、s10などの信号に応じて 10, 60, 0のいずれかが与えられます。
またカウントダウン終了を検出するために、 時間カウント値cntが0となったときに、カウントダウン終了を 示す出力done=1とするために、cntと0とをコンパレータCMPによって 比較しています。
entity datapath is
  port(
    clk, rst, p10, p60, m1: in std_logic;
    done: out std_logic;
    cnt: out std_logic_vector(11 downto 0));
end datapath
architecture arch of datapath is
  signal reg12_out, reg12_in: std_logic_vetor(11 downto 0);
  signal sel_out: std_logic_vector(6 downto 0);
  function sign_extend (a: std_logic_vector(6 downto 0))
    return std_logic_vector is
  begin
    if (a(6) = '1') then return("11111" & a);
    else return("00000" & a);
    end if
  end sign_extend
begin
  process (p10, p60, m1) begin -- selector
    if (p10 = '1') then sel_out = "0001010";    -- "10"
    elsif (p60 = '1') then sel_out = "0111100"; -- "60"
    elsif (m1 = '1') then sel_out = "1111111";  -- "-1"
    else sel_out = "0000000";
    end if;
  end process;
  process (reg12_out, sel_out) begin -- adder
    reg12_in <= reg12_out + sign_extend(sel_out);
  end process;
  process (clk, rst) begin -- register
    if (rst = '0') then reg12_out = "000000000000";
    elsif (clk'event and clk = '1') then re12_out <= reg_in;
    end if;
  end process;
  process (reg12_out) begin -- comparator
    if (reg12_out = "000000000000") then done <='1';
    else done <= '0';
    end if
  end process;
  cnt <= reg12_out;
end arch;
この制御部のVHDL記述は次のようになります。(p.103: 要点のみ抜粋) 前半のcase文が、まさに状態遷移図に対応している 次状態決定回路になっていて、 また後半のcase文が、出力決定部になっていることに注意しましょう。 またステートマシンの本質である状態遷移は、2つ目のprocess文で 記述されていることにも注意しておきましょう。
entity control is
  port(
    clk, rst, set, run, s10, s60, done: in std_logic;
    p10, p60, m1: out std_logic);
end control;
architecture arch of control is
  type t_state is (IDLE_ST, SET_ST, RUN_ST, DONE_ST);
  signal state, next_state: t_state;
begin
  process (state, set, run, done) begin -- next state calc
    case state is
      when IDLE_ST =>
        if (set = '1') then next_state <= SET_ST;
        else next_state <= IDLE_ST;
        end if;
      when SET_ST =>
        if (run = '1') then next_state <= RUN_ST;
        else next_state <= SET_ST;
        end if;
      when RUN_ST =>
        if (done = '1') then next_state <= DONE_ST;
        else next_state <= RUN_ST;
        end if;
      when DONE_ST => next_state <= IDLE_ST;
    end case;
 end process;
  process (clk, rst) begin -- state register
    if (rst = '0') then state <= IDLE_ST;
    elsif (clk'event and clk = '1') then state <= next_state;
    end if;
  end process;
  process (state, s10, s60, done) begin -- control signals
    p10 <= '0'; p60 <= '0'; m1 <= '0';
    case state is
      when IDLE_ST => null;
      when SET_ST =>
        if (s10 = '1') then p10 <= '1';
        elsif (s60 = '1') then p60 <= '1';
        end if;
      when RUN_ST =>
        if (done /= '1') then m1 <= '1'; -- /= : Not Equal ※←教科書の記述が間違っているようです
        end if;
      when DONE_ST => null;
    end case;
  end process;
end arch;
entity timer is
  port(
    clk, rst, set, run, s10, s60: in std_logic;
    done: out std_logic;
    cnt: std_logic_vector(11 downto 0));
end timer;
architecture arch of timer is
  component control
    port(
      clk, rst, set, run, s10, s60, done: in std_logic;
      p10, p60, m1: out std_logic);
  end component;
  component datapath
    port(
      clk, rst, p10, p60, m1: in std_logic;
      done: out std_logic;
      cnt: out std_logic_vector(11 downto 0));
  end component;
  signal p10, p60, m1, done_tmp: std_logic;
begin
  i0: control port map(
    clk, rst, set, run, s10, s60, done_tmp, p10, p60, m1);
  i1: datapath port map(
    clk, rst, p10, p60, m1, done_tmp, cnt);
  done <= done_tmp;
end arch;  
ただし、一般に、このような構成を意識しない記述から 論理合成によって得られる回路は、構成を意識した記述から 得られる回路よりも、「質」が低くなる傾向があります。 すなわち回路規模が大きく、消費電力が大きくなる傾向があります。 そのため、なるべくデータパスと制御部を分けて記述するほうが、 とりあえず動く回路、では不十分な場合で、 より質の高い論理回路を得たい場合は、得策となるようです。
architecture arch2 of timer is
  type t_state is (IDLE_ST, SET_ST, RUN_ST, DONE_ST);
  signal state: t_state;
  signal cnt_tmp: std_logic_vector(11 downto 0);
  signal sel_out: std_logic_vector(6 downto 0);
begin
  process (clk, rst) begin
    if (rst = '0') then
      cnt_tmp <= "000000000000";
      done <= '0';
      state <= IDLE_ST;
    elsif (clk'event and clk = '1') then
      case state is
        when IDLE_ST =>
          if (set = '1') then state <= SET_ST;
          else state <= IDLE_ST;
          end if;
        when SET_ST =>
          if (s10 = '1') then
            cnt_tmp <= cnt_tmp + "000000001010";
          elsif (s60 = '1') then
            cnt_tmp <= cnt_tmp + "000000111100";
          end if;
          if (run = '1') then state <= RUN_ST;
          else state <= SET_ST;
          end if;
        when RUN_ST =>
          cnt_tmp <= cnt_tmp - "000000000001";
          if (cnt_tmp = "000000000001") then
            done <= '1';
            state <= DONE_ST;
          else state <= RUN_ST;
          end if;
        when DONE_ST =>
          done = '0';
          state <= IDLE_ST;
      end case;
    end if;
  end process;
  cnt <= cnt_tmp;
end arch2;