Quartus Lite で使えたVHDL-2008機能

# 始めに
久しぶりにCycloneVでVHDLの案件が来たので、Quartus Prime 21.1 Liteで使えるVHDL-2008機能を試してみました。
公式の機能比較チャートではLite EditionではVHDL-2008はサポートしないという事になっているのですが、VHDLの言語バージョンを2008に設定すれば使えるようになっています。
結論から書くと非常に残念なサポート状況になっていますので、その辺りを説明します。
# 目次
* [VHDL-2008の有効化方法](#vhdl-2008の有効化方法)
    * [全ファイルをVHDL-2008に設定する](#全ファイルをvhdl-2008に設定する)
    * [ファイル毎にVHDLバージョンを設定する](#ファイル毎にvhdlバージョンを設定する)
* [使えたVHDL-2008機能](#使えたvhdl-2008機能)
    * [Unconstrained elements in arrays](#unconstrained-elements-in-arrays)
    * [Matching equality/inequality operators (使用不可)](#matching-equalityinequality-operators-使用不可)
    * [Condition operator](#condition-operator)
    * [Matching case statement](#matching-case-statement)
    * [Simplified sensitivity lists](#simplified-sensitivity-lists)
    * [Extensions to the generate statement](#extensions-to-the-generate-statement)
    * [Enhanced Bit-string literals](#enhanced-bit-string-literals)
    * [Block comments](#block-comments)
    * [Reading output port (一部使用可能)](#reading-output-port-一部使用可能)
* [特に使えないVHDL-2008機能](#特に使えないvhdl-2008機能)
    * [IEEE.std_logic_1164,IEEE.numeric_stdがVHDL-93のまま](#ieeestd_logic_1164ieeenumeric_stdがvhdl-93のまま)
* [まとめ](#まとめ)

# VHDL-2008の有効化方法
VHDLのデフォルトはVHDL-93なので、VHDL-2008を使用するには設定で有効化する必要があります。
全ファイルを設定する方法と、ファイル毎に設定する方法があります。
## 全ファイルをVHDL-2008に設定する
SettingsダイアログのCategory-VHDL Inputページで、VHDL versionからVHDL2008を選択するとVHDLのデフォルトバージョンがVHDL-2008になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256050/9f468e2a-c4f6-05f4-6d29-fba145b612c2.png)
## ファイル毎にVHDLバージョンを設定する
SettingダイアログのCategory-Filesページで、設定したいファイルを選択してPropertyボタンをクリックするとFile Propertyダイアログが表示されるのでHDL versionからVHDL_2008を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256050/ca66f73e-429b-2e41-70f4-3483f86d4898.png)
#使えたVHDL-2008機能
公式のサポート機能一覧 [Intel® Quartus® Prime Support for VHDL 2008](https://www.intel.com/content/www/us/en/programmable/quartushelp/current/index.htm#hdl/vhdl/vhdl_list_2008_vhdl_support.htm)
このリストに沿ってVHDL-2008機能を説明します。

| 機能 | 使用可能 |
|:-|:-:|
| [Unconstrained elements in arrays](#unconstrained-elements-in-arrays) | ○ |
| [Matching equality/inequality operators](#matching-equalityinequality-operators-使用不可)  | × |
| [Condition operator](#condition-operator)  | ○  |
| [Matching case statement](#matching-case-statement)  | ○  |
| [Simplified sensitivity lists](#simplified-sensitivity-lists)  | ○  |
| [Extensions to the generate statement](#extensions-to-the-generate-statement)  | ○  |
| [Enhanced Bit-string literals](#enhanced-bit-string-literals) | ○ |
| [Block comments](#block-comments) | ○ |
| [Reading output port](#reading-output-port-一部使用可能)| △ |
## Unconstrained elements in arrays
配列の要素として、未制約の配列を使用可能。
「未制約の配列」とは範囲を決めていない配列の事。
この機能では配列の配列が簡単に使えるようになります。

```VHDL
--未制約のstd_logic_vectorの配列      ここの範囲をオープンに出来る↓
type slv_array is array (natural range <>) of std_logic_vector;
--任意の幅のレジスタ配列
signal registers : slv_array(0 to 3)(15 downto 0);

--VHDL-93ではこう書かなければならず、要素の幅毎に型の定義が必要
type slv16_array is array (natural range <>) of std_logic_vector(15 downto 0);
signal registers : slv16_array(0 to 3);


--slv_arrayをパッケージで宣言すればポートにも使える
entity reg_box is
  generic (
    C_REG_WIDTH := 16;
    C_REG_NUM   := 4
  );
  port (
    --パラメータ指定可能な任意の幅・サイズの配列
    regsters : out slv_array(0 to C_REG_NUM-1)(C_REG_WITDH-1 downto 0)
  );
```
## Matching equality/inequality operators (使用不可)
booleanを返す一致`=` ・不一致`/=`演算子に対して、`std_logic`を返す一致`?=`・不一致`?/=`演算子です。
std_logicでは`'H'`と`'1'`・`'L'`と`'0'`が一致として判定され、`'-'`はdon't careと判定されます。
残念ながら使用不可です。
2項演算子として`?=`・`?/=`は存在するのですが、 **std_logic_1164がVHDL-93のまま** のようで`std_logic/vector`に対して演算子がオーバーロードされていない事が原因です。[後述します](#ieeestd_logic_1164ieeenumeric_stdがvhdl-93のまま)

```VHDL
  signal eq : std_logic;
  signal a  : std_logic_vector(3 downto 0) := "0011";
  signal b  : std_logic_vector(3 downto 0) := "-L1H";

  eq <= a ?= b; --eq = '1'となるはず
  --Error (10327): VHDL error at VHDL2008.vhd(54): can't determine definition of operator ""?="" -- found 0 possible definitions
```
## Condition operator
新しい演算子`??`で`std_logic`を`boolean`に変換します。
値が`'1'`/`'H'`なら`true`、それ以外は`false`になります。
明示的に使わなくても、if文の条件式等のbooleanが要求される式に`std_logic`の値を指定すると、暗黙的に`boolean`に変換します。

```VHDL
  signal a : std_logic;
  signal b : std_logic;

  -- (a and b) = 1 か？
  if ?? (a and b) then
  --こう書いても同じ
  if a and b then

  -- a = 1か？
  if a then

  --条件代入の条件でも使用可
  b <= '0' when a else 'Z';  --a=1なら'0'、そうでなければ'Z'
```

## Matching case statement
選択肢に`'-'`(don't care)を指定可能なcase文です。優先度付きマルチプレクサが簡単に書けます。

```VHDL
  signal ptn : std_logic_vector(3 downto 0);

  case? ptn is
  when "---1" => q <= data(0);
  when "--10" => q <= data(1);
  when "-100" => q <= data(2);
  when "1000" => q <= data(3);
  when others => q <= (others => '0');
  end case?
```

## Simplified sensitivity lists
センシティビティリストに`all`と書くことで省略出来ます。Verilogの`always @*`と同じです。

```VHDL
  process (all) begin
    ... --組合せロジック
  end process;

  --クロックも使用可能
  process (all) begin
    if RST then
      --非同期リセット
    elsif rising_edge(CLK) then
      --レジスタ
    end if;
 end process;
```

## Extensions to the generate statement
`if-generate`でも通常のif文と同じく、`else/elsif`が使えるようになりました。
今までは`else`が使えなかったので、同じ条件の逆を書く必要がありましたが簡略化出来ます。
さらに、`case-generate`が追加されました。

```VHDL
  gen_debug: if C_DEBUG >= 3 generate
    --デバッグレベル3以上
  elsif C_DEBUG = 2 generate
    --デバッグレベル2
  else generate
    --上記以外の場合
  end generate;

  gen_loop: for I in 0 to 5 generate
    --case-generate
    gen_case: case I generate
    when 0 to 2 =>
      --0-2の場合
    when 3 | 5 =>
      --3,5の場合
    when others =>
      --上記以外
    end generate;
  end generate;

```

## Enhanced Bit-string literals
`bit_vector`/`std_logic_vector`の基数・幅指定機能です。
VHDL-93では16進の場合は文字数に応じて4bit単位でしか指定出来ませんでした。
`'_'`を区切り文字としても使用可能です。
Verilogの`9'h123`等と同じです。

```VHDL
   6x"1F" -- "011111"
   6x"F"  -- "001111" 上位2bitゼロ拡張される
   6x"FF" -- エラー 符号無しで上位の'1'のビットが切捨てられる場合はエラー
  6sx"F0" -- "11000" 符号ありで上位の'1'のビットが切捨てられる場合はOK

  b"0001_0011" -- 基数を指定すれば区切り文字を使用可能
   "0001_0011" -- エラー 区切り文字を使用する場合は基数の指定が必要
```

## Block comments
Cと同じ /* ～ */ のブロックコメントです。
QiitaのVHDLシンタックスはVHDL-2008に対応してないですね。

```VHDL
    signal truth : std_logic;
--  signal dummy : std_logic;
    signal d0    : std_logic;
/*  signal d1    : std_logic;
    signal d2    : std_logic;
*/
```

## Reading output port (一部使用可能)
出力ポートの参照です。出力ポートと同じ内部信号を宣言しなくても良くなります。
公式のサポート一覧には記載されてませんが、一応使用可能です。
数式等で直接出力ポートを参照する事は可能ですが、**サブモジュール・サブプログラムに接続するとエラー**になってしまい、使い辛いです。
Lite Editionではなく**Standardならエラーにならない**らしいです。

```VHDL
  entity clock_div is
    port (
      CLK_in   : in  std_logic;
      CLK_div2 : out std_logic;
      CLK_div4 : out std_logic
    );
  end;

  process (CLK) begin
    if rising_edge(CLK) then
      CLK_div2 <= not CLK_div2;  --出力ポートの参照
    end if;
  end process;

  process (CLK_div2) begin
    if rising_edge(CLK_div2) then  --functionにCLK_div2を渡すとエラー
--  Error (10600): VHDL error at VHDL2008.vhd(94): can't read value of interface object "CLK_div2" of mode OUT

    if CLK_div2'event and CLK_div2 = '1' then  --直接参照はOK
      CLK_div4 <= not CLK_div4;  --出力ポートの参照
    end if;
  end process;
```
#特に使えないVHDL-2008機能
上記で説明した以外の機能は当然使えない訳ですが、さらなる落とし穴があるので説明します。
## `IEEE.std_logic_1164`,`IEEE.numeric_std`がVHDL-93のまま
VHDLのバージョンで2008を指定しても、使用されるライブラリはVHDL-93バージョンのままです。
理由はQuartusのVHDL-2008サポート範囲では、これらの**ライブラリ自体をコンパイル出来ない**からです。
具体的には以下の機能が不足していることが原因です。

* Matching relational operator (`?>`, `?<`, `?<=`, `?>=` 等)
* resolve関数を配列の要素毎に適用する (`std_logic_vector` と `std_ulogic_vector` が完全に別の型のまま)
* 単項論理演算子 (Verilogのリダクション演算と同じ `and A` で配列Aの全要素をAND演算できる)

QuartusのインストールディレクトリにはVHDL-2008版IEEEライブラリのソースが入っていますが、残念ながら使われていません。
ですので、VHDL-2008で追加された`IEEE.numeric_std_unsigned`は存在しません。
Synopsisの`std_logic_unsined`を使う必要があります。

[Matching equality/inequality operators](#matching-equalityinequality-operators-使用不可)(`?=`, `?/=`)が演算子としては存在するのに実質使えないのもこれが原因です。
`?=`,`?/=`の実際の動作はVHDL-2008版の`std_logic_1164`で定義されているためです。
同じ理由で、VHDL-2008で追加された`std_logic`と`std_logic_vector`との論理演算も使えません。

```VHDL
signal slv    : std_logic_vector(3 downto 0);
signal logic  : std_logic;
signal slv_and: std_logic_vector(3 downto 0);

slv_and <= slv and logic;  --slvの全要素に対してand logicを適用する
--Error (10476): VHDL error at test.vhd(19): type of identifier "logic" does not agree with its usage as "std_logic_vector" type
```

演算子については自分のパッケージで定義すれば使えるようになります。
`?=`, `?/=` や `std_logic and std_logic_vector` の様なスカラと配列の論理演算は自分でオーバーロードすることで使えるようになります。
まあ、そこまでするかと思いますが…

```VHDL
function "and" (L: std_logic_vector; R: std_logic) return std_logic_vector is
  alias    lv  : std_logic_vector(L'length-1 downto 0) is L;
  variable ret : std_logic_vector(L'length-1 downto 0) is L;
begin
  for I in ret'range loop
    ret(I) := lv(I) and R;
  end loop
  return ret;
end;
```
#まとめ
VHDL-2008が使えると言っても、非常に限定されたサポートでした。
特にIEEEの標準ライブラリが使えないのはかなり痛いですね。
VHDL-2008を使いたい人が自分以外にどれだけいるか分かりませんが、役に立てば嬉しいです。
遠慮無くコメント下さい。