VHDLの構文(1)

VHDLの書き方を解説します。Verilogは別途全く同じ内容で記載いたします。
HDLに限らずプログラム言語なども全体を通して書き方のルールがあります。VHDLの仕様書や解説書を読んでいると書き方がいろいろあるのに気がつくと思います。おそらく、エンジニアによっても書き方が違うと思います。初心者がその多くの書き方を見てしまい、どれが正解なんだ。と考え込む傾向にあります。なので、あまり考えないで、人のスタイルを真似てみてください。
会社によっては、スタイルのルールがある場合がありますので、それに従うようにしてください。
次に示すのは、私のルールです。ソースコードの中に — がありますが、– 以降のテキストはコメントとして判断されます。
-- TITLE: "datasel.vhd" -- MODULE NAME: datasel -- PROJECT CODE: -- AUTHOR: (****@nakaharagiken.com) -- CREATION DATE: 2020.1.1 -- SOURCE: -- LICENSE: Copyright (c) 2020 ***************. All rights reserved. -- DESCRIPTION: 2系統の信号から1系統を選択。レジスタ付き -- NOTES TO USER: 入力信号は予めチャタリング処理しておくこと -- SOURCE CONTROL: -- REVISION HISTORY: v0.1 2020.1.2 First edition -- -- -- -- -- -- -- -- library IEEE; -- [構文] ライブラリ宣言 use IEEE.std_logic_1164.all; -- [構文] パッケージ呼び出し use IEEE.std_logic_unsigned.all; -- [構文] パッケージ呼び出し use IEEE.std_logic_arith.all; -- [構文] パッケージ呼び出し entity datasel is -- [構文] エンティティ宣言:エンティティ名 port ( -- [構文] エンティティ宣言:ポート宣言 clock: in std_logic; -- [構文] エンティティ宣言:入出力信号名 nreset: in std_logic; -- [構文] エンティティ宣言:入出力信号名 enable: in std_logic; -- [構文] エンティティ宣言:入出力信号名 SwitchIn_A: in std_logic; -- [構文] エンティティ宣言:入出力信号名 SwitchIn_B: in std_logic; -- [構文] エンティティ宣言:入出力信号名 input_sel: in std_logic; -- [構文] エンティティ宣言:入出力信号名 output_ans: out std_logic -- [構文] エンティティ宣言:入出力信号名 ); -- [構文] エンティティ宣言:ポート宣言の終わり end entity datasel; -- [構文] エンティティ宣言の終わり architecture rtl of datasel is -- [構文] アーキテクチャ名宣言 --------------------------------------------------------------- -- SIGNALS --------------------------------------------------------------- signal wInputSeldA: std_logic; -- [構文] アーキテクチャ:ノード宣言:モジュール内信号定義 signal wInputSeldB: std_logic; -- [構文] アーキテクチャ:ノード宣言:モジュール内信号定義 signal wnInputSel: std_logic; -- [構文] アーキテクチャ:ノード宣言:モジュール内信号定義 signal wSeldSignal: std_logic; -- [構文] アーキテクチャ:ノード宣言:モジュール内信号定義 signal rSeldSignal: std_logic; -- [構文] アーキテクチャ:ノード宣言:モジュール内信号定義 begin -- [構文] アーキテクチャ:論理記述開始 wnInputSel <= not input_sel; -- 論理ゲート記述 wInputSeldA <= SwitchIn_A and input_sel; -- 論理ゲート記述 wInputSeldB <= SwitchIn_B and wnInputSel; -- 論理ゲート記述 wSeldSignal <= wInputSeldA or wInputSeldB; -- 論理ゲート記述 ------------------------------------------ -- rSeldSignal レジスタ ------------------------------------------ process (clock,nreset) begin -- DFF記述 if nreset = '0' then -- リセット初期化 rSeldSignal <= '0'; -- リセット時の初期値挿入 elsif clock' event and clock = '1' then -- clock 立ち上がり指示 if (enable ='1') then -- イネーブル処理 rSeldSignal <= wSeldSignal; -- D端子の入力接続 end if; -- イネーブル処理内完了 end if; -- クロック立ち上がり処理完了 end process; -- DFF記述終了 output_ans <= rSeldSignal; -- 論理ゲート記述:出力信号接続 end architecture rtl; -- [構文] アーキテクチャ名宣言:終わり
コメントに[構文]と記した部分があります。これは、VHDLのルール部分です。そしてほとんどの場合、[構文]と書いた部分は順番も大切です。
たとえば、最初に出てくる[構文]はライブラリ宣言ですが、これはこの場所に書く必要がある。という事です。
ちょっと戻って、1〜19行目にコメントが長く記載されていますが、これは私のルールです。すべてのソースコードにこのようなコメントをヘッダーとして記載しています。このようなヘッダーは無くても良いのですが、ソースコードの管理の意味でも同様のヘッダーを記述する事をお勧めします。
ライブラリ宣言
ライブラリ宣言の所で、行の先頭から「library」という記述がありますが、これはVHDLの予約語という特別な単語です。プログラムをコーディングする方であれば、言語の予約語と同じ意味です。予約語とは言語の仕様の中で予め決めた単語です。主に言語への命令がありますが、論理式のand,orなども予約語として分類されます。
予約語は回路を設計していくうちに自然と覚えてしまいますが、XilinxやIntelなどの統合開発環境でHDLを記述すると、予約語をハイライトする機能がありますので、それを見ながら覚えるのが良いでしょう。
そして、予約語は言語への命令になりますので、ユーザがそれ以外の目的で使う事はできません。たとえば、入出力信号名やモジュール内信号定義の名前に使えません。
20〜23行目はライブラリ宣言ですが、これはVHDL言語へのおまじない。として、初心者は必ず記述するようにしてください。
エンティティ宣言
26〜36行目は入力信号と出力信号の記述でエンティティ宣言といいます。最初のうちは、1つのファイルで1つのエンティティにしてください。
エンティティは設計者が作る部品を意味しています。エンティティは設計者の裁量で小さくする事も大きくする事もできます。
そして、エンティティの中にエンティティを作る事ができます。そのことを階層構造とか階層設計といいます。この時、エンティティに上下関係ができるのですが、エンジニアは上の階層を「親」下の階層を「子」という呼び方をします。「BのエンティティはAの子供だ」みたいな会話をします。
親子関係の記載方法はテストベンチにも関係しますので、機会を改めて記載する事にいたします。
アーキテクチャ名宣言
38行目からがこのエンティティの本体になります。38行目でアーキテクチャの名前を宣言しています。
エンティティとアーキテクチャがわからない。という質問をよく受けます。エンティティは外観、アーキテクチャは中身。と思えば間違いありません。
シグナル宣言
42〜46行目でエンティティで使うワイヤーとレジスタを宣言しています。このモジュールでは1個1個宣言していますが、ワイヤーもレジスタも束ねて宣言する事ができます。初心者にはこの束ねるという概念がなかなかわかりにくいようなので、これも改めて説明するようにします。
このシグナル宣言は1行に1つ宣言していますが、1行に何個も連ねて宣言す流事もできます。例えば、
signal wInputSeldA,wInputSeldB,wnInputSel std_logic;
という書き方です。むしろ、こちらの書き方で記述するエンジニアの方が多いかもしれません。私はエンティティ宣言とシグナル宣言は1行に1アイテムと決めています。それは、コメントを書くためです。
後からVHDLのソースコードを見直した時に、一番最初に読むのはエンティティ宣言とシグナル宣言です。そこから、どのような動作なのかを思い出すのですが、コメントが無いとかなり辛い思いをします。半年後の自分は他人と思えるぐらい、ソースコードを覚えていないものです。
論理記述、レジスタ記述
48行目から論理記述です。beginで始まってend architecture rtl;までが記述になります。
49〜52行目に論理ゲート記述というコメントがありますが、これはブール代数式をVHD風に記述したものになります。VHDLはブール代数の書き方そのものなのでわかりやすいと思います。
57〜65行までがDFFの記述です。processで始まってnd process;で終わっています。このような記述を見つけて、初めてrSeldSignalはDFFなのだな。とわかるのがVHDLです。
57行目のprocess (clock,nreset) beginを見てください。括弧の中に、クロックとリセットが入っています。この書き方はそっくり真似てください。稀に括弧の中にクロックだけ記述する人がいますが、きちんと括弧の中にリセットも入れて、(FPGAにリセットを入れて)初期値を持たせるように習慣付ける事をお勧めします。
57行目は重要なので、もう少し解説します。process (clock,nreset) beginは読み解くと、「clockとnresetが変化した時の動きをbegin以降に記載する」という意味です。process分の括弧の中にはセンシティビティ・リストという名前がついています。57行目だけでは、”0″から”1″に変化したのか”1″から”0″に変化したのかは記載されていません。とにかく、どっちでもいいから動いたら。という意味になります。
58〜64行目は先ほどのprocess (clock,nreset) beginで実行する内容です。まず、58行目でnreset = ‘0’の時なので、nresetが動いて、かつ’0’の時。という事になります。つまり、クロックは無視されています。よって、58〜59行目まではクロックに同期していないので、非同期の回路。という事になります。一般的にリセットは非同期で扱います。
60行目が重要です。elsif clock’ event and clock = ‘1’ then はクロックが動いて、なおかつそれが’1’に向かっている時。という意味です。つまり立ち上がりエッジを意味します。最近のVHDLの書き方で、rising_edge(clock)と記述する方が多いのですが、clock’ event and clock = ‘1’ と rising_edge(clock)は同じ意味ではありませんので注意してください。ただし、論理合成をした時には同じになると思います。確認した事ないですが・・・結局どっちでも良いのか?
61行目でenableの記述が出てきました。これも改めて説明する必要があるので、今はenable=1の時に動作する。という事を確認しておいてください。
62行目で初めてDFFの入力にwSeldSignalが接続された事になります。
追々各項目を説明していきますが、ここで覚えるのは「目次」で示した各構文の場所(順序)です。しかし、がんばって覚える必要はありません。このソースコードをコピーして、自分の好きなようにアレンジしていけば、おのずと順序は同じになります。そのようなコーディングを雛形を使う。といいますが、それで問題ありません。
プロの現場でも、昨日までVerilogで今日からVHDLなんて時には、雛形を持ってきて、コーディングを始めます。その方が間違いが無く、仕事が早いですから。
-
前の記事
フリップフロップ(2) 2020.06.18
-
次の記事
Verilogの構文(1) 2020.06.20

コメントを書く