ずっとFPGA初心者（その12：関数）

# VHDLで関数を使う
今回のお題は、VHDLで関数を扱うもの。

## 環境
[その１](https://qiita.com/infinite1oop/items/4b0f03b97ed859fbd954)からずっとOdyssey MAX10 FPGA Eval KitおよびQuartus利用。

## サンプル
### LEDを用いてストップウォッチを実現
ボタン0（BTN0）押下でスタート、再押下でラップ、再押下でストップ（リセット）する。
　STOP -> RUNNING -> LAP (RUNNING) -> STOP -> RUNNING -> .... 
という状態遷移（繰り返し）となる。ボタン１（BTN1）が押下されていないときには、スタートからの経過時間（秒）をLED表示、ボタン１が押下されたときには、ラップ時間をLED表示するものとする。

### 関数の利用
スタートからの経過時間（秒）およびラップ時間（秒）をLEDで表現するところに関数を用いる。表現方法としては、[前回その11の秒カウンタ](https://qiita.com/infinite1oop/items/98e928f8252726937113)と同じ方式を用いる。なお、あえて関数化する必要はないレベルではあるが、単純な例の方が関数を説明する上でわかりやすいであろう。

## functionとprocedure
関数化の方法として、VHDLには、functionとprocedureとがあるらしい。今回はprocedureを利用。今回、VHDLの関数について参考にしたサイトは次のとおり。
- [functionとprocedureの違い1](https://qiita.com/ryo_i6/items/49f17150faeb3423fe8d)
- [functionとprocedureの違い2](https://vhdlguide.readthedocs.io/en/latest/vhdl/package.html)
- [参照にしたサンプルコード1](https://vhdlwhiz.com/using-procedure/)
- [参照にしたサンプルコード2](https://www.ics.uci.edu/~jmoorkan/vhdlref/procedur.html)

## VHDLコード
### ライブラリおよび信号定義
```vhdl
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all; -- necessary for conv_std_logic_vector

entity ProcTest is
  port (
    CLK, BTN0, BTN1 : in  std_logic;
    LED0, LED1, LED2, LED3, LED4, LED5, LED6, LED7 : out std_logic);
end;
```

ここは自明なので、説明省略。

### 関数部分
```vhdl
architecture RTL of ProcTest is

procedure p_Disp (
  constant p_NUM : in  integer;
  signal p_OUT : out std_logic_vector(7 downto 0) ) is
  begin
  p_OUT <= not conv_std_logic_vector(p_NUM,8);
end p_Disp;
```

procedure利用。次の引数がある。
- p_NUM：入力 - LEDで表現したい数値
- p_OUT；出力 - ビット変換した結果（呼び出し側でLEDに代入）
conv_std_logic_vector()を使って、数値をビット値へ変換する。

### 変数定義
```vhdl
signal LED: std_logic_vector(7 downto 0) := "11111111"; -- Off
constant CYCLE : integer := 50000000;
signal CNT : integer range 0 to CYCLE-1;
signal SEC : integer := 0;
signal LAP : integer := 0;
signal FLAG: integer := 0;  -- 0:Stop 1:Start->Running 2:Lap(&Running) 
```

- LED：LED用のビット列（初期値OFF、負論理）
- CYCLE：1秒（50MHz)
- CNT：クロック用カウンタ
- SEC：秒カウンタ
- LAP：ラップ値
- FLAG：状態遷移フラグ（0：停止 → 1：実行中 → 2：ラップかつ実行中 → 0 → 1 → 2 → 0 ...）

### メインルーチン
```vhdl
begin
  LED0 <= LED(0); LED1 <= LED(1); LED2 <= LED(2); LED3 <= LED(3);  -- ①
  LED4 <= LED(4); LED5 <= LED(5); LED6 <= LED(6); LED7 <= LED(7);  -- ②

  process (BTN0)  -- Removal of chattering should be considered -- ③
  begin
    if (BTN0'event and BTN0='0') then
      if (FLAG = 0) then     -- Stop
        FLAG <= 1;           -- to Running
      elsif (FLAG = 1) then  -- Running
        FLAG <= 2;
        LAP <= SEC;          -- Record Lap  -- ④
      else  -- FLAG = 2      -- Running    
        FLAG <= 0;           -- to Stop
        LAP <= 0;            -- Init Lap
      end if;
    end if;
  end process;

  process (SEC, BTN1)  -- ⑤
  begin
--    if (BTN1'event and BTN1='0') then -- NG -- ⑥ 文法エラー
    if (BTN1='0') then
      p_Disp(LAP, LED);  -- ⑦
    else
      p_Disp(SEC, LED);  -- ⑧
    end if;
  end process;

  process (CLK)  -- ⑨
  begin
    if (FLAG = 1 or FLAG = 2) then  -- Runinng -- ⑩
      if (rising_edge(CLK)) then
        if (CNT = CYCLE-1) then
          CNT <= 0;
	       SEC <= SEC+1;
        else
          CNT <= CNT+1;
        end if;
      end if;
    else -- FLAG = 0 (Stop) -- ⑪
      CNT <= 0;
      SEC <= 0;
     end if;
  end process;
end RTL;
```

- ①、②、③、⑤、⑨：同時に処理される
- ①、②：LED用ビット列をLED信号に代入
- ③：BTN0押下毎の状態遷移、実行中に押下されると、秒カウンタをLAPに代入（④）（チャタリングは未考慮）
- ⑤：LEDによる数値表示を関数「p_Disp」により毎秒実行、押下時はLAP表示（⑦）、非押下時は経過秒SEC表示（⑧）
- ⑥：この直後の行の代わりにこの行を用いると文法エラーとなる
- ⑨：実行中はクロックカウンタおよび秒カウンタインクリメント（⑩）、非実行中は両カウンタ初期化（⑪）

## ピンアサイン
![スクリーンショット 2021-06-06 16.38.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/6b108cba-36b1-177a-21e0-f825ef23c25f.png)

## 実験
### BTN1押下
LAPを表示（ここでは5）。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/0e13176f-fe85-adfd-6d92-cd7b0c8c0a95.png", width=80%>

### BTN1非押下
SEC（経過秒）を表示（ここでは4）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/1b59e01e-e9b9-0119-25e4-fd63fc5635e4.png", width=80%>