第6回: 組み合わせ論理回路のHDL記述(2)

今回は、組み合わせ論理回路のHDL記述例の続きとして、 演算器をとりあげてみましょう。

組み合わせ論理回路のHDL記述

加算器

[p.71] 2つの数値の加算を行う回路を加算器と呼びます。 ここでは加算対象の数値を8ビットとし、桁上げ入力ci、桁上げ出力coを 備えた8ビットの加算器を動作記述で書いてみましょう。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity add is
  port (
    a, b: in std_logic_vector(7 downto 0);
    ci: in std_logic;
    co: out std_logic;
    x: out std_logic_vector(7 downto 0)
  );
end add;

architecture arch of add is
begin
  process (a, b, ci)
    variable tmp: std_logic_vector(8 downto 0);
  begin
    tmp := ("0" & a) + ("0" & b) + ("00000000" & ci);
    co <= tmp(8);
    x <= tmp(7 downto 0);
  end process;
end arch;
第2回の最後のほうで、4ビット加算器の 動作記述の例をみていましたが、それとほぼ同様です。 (ちなみにこの例では、記述の仕方は「加算を行う」ということを 書いている動作記述なのに、architecture名がarchになっています。 実はarchitecture名は、必ずしも記述内容と一致する必要はなく、 自分でわかる名前(文字列)でかまいません。 ただ、この例では、「加算器の動作記述」ですので、 本当は"architecture behv of add is..."と書いておいたほうが、 あとあと混乱しにくいとは思います。)

この調子でいけば、何ビットの加算器でもかけそうですね。 ちなみに、加算結果を途中で入れている変数tmpは、vector形式で 8 downto 0、つまり9ビットになっています。 この理由を考えてみましょう。

※教科書のVHDL記述には書いてありませんが、 演習で使っているXilinx ISEでは、 最初の方のおまじないのところに、 use ieee.std_logic_unsigned.all; が必要なようです。

シフタ

[p.73] 入力を、指定したビット数だけ左または右にずらす(シフトする)回路を シフタ(shifter)と呼びます。 d=0のときは右へ、d=1のときは左へ、nで指定したビット数だけ シフトする回路をVHDLで記述すると以下のようになります。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity shift is
  port (
    a: in std_logic_vector(7 downto 0);
    d: in std_logic;
    n: in_std_logic_vector(2 downto 0);
    x: out std_logic_vector(7 downto 0);
  );
end shift;

architecture arch of shift is
begin
  process (a, d, n) begin
    if (d = '1') then x <= sh_left(a, to_integer(n));
    else x <= sh_right(a, to_integer(n));
    end if;
  end process;
end arch;
この例では、sh_left, sh_rightというものが使われていますが、 これは使っているライブラリであるieee.std_logic_arithで 定義されているもので、それぞれ指定したビット数だけ 左、右へシフトする、というものです。 こういうのが最初からあると便利ですね。 シフトするビット数は、整数型で指定する必要があるので、 これもライブラリの中で定義されているto_integerを使っています。 ちなみにシフトするビット数nが3ビットである理由を考えてみましょう。

※このsh_left, sh_rightは、演習で用いているXilinx ISEでは 使用できないようです。 いまのところ、Xilinx ISEでは、指定したビット数だけシフトする関数は ないようです・・・(そんなはずはないような気がするのですが・・・) このシフタは、VHDL記述の例としてだけ見ておいてください。

乗算器

[p.74] 2つの数の乗算を行う乗算器は、詳しくは次回紹介しますが、 まともに論理回路として設計すると、非常に複雑になりますが、 HDLであれば、次のように簡単に済んでしまいます。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity mult is
  port (
    a, b: in std_logic_vector(7 downto 0);
    x: out std_logic_vector(15 downto 0);
  );
end mult;

architecture arch of mult is
begin
  process (a, b) begin
    x <= a * b;
  end process;
end arch;
ここから、複雑な乗算器の実際の論理回路は、 論理合成ツールが自動的に作ってくれるわけです。

パリティ・ジェネレータ

[p.77] 入力の数を2進数としてみて、そのビットの中に1の数が 奇数個あれば1を、偶数個あれば0を出力する回路を (奇数)パリティ・ジェネレータ、と呼びます。 これは、数値中の1ビットの誤りを検出することが可能な符号を 生成するために用いられます。
library ieee;
use ieee.std_logic_1164.all;

entity parity is
  port (
    a: in std_logic_vector(7 downto 0);
    x: out std_logic;
  );
end parity;

architecture arch of parity is
  -- bit reduction xor
  function br_xor(a: std_logic_vector) return std_logic is
    variable tmp:std_logic := '0';
  begin
    for i in a'range loop
      tmp := tmp xor a(i);
    end loop
    return(tmp);
  end br_xor;

begin
  process (a) begin
    x <= br_xor(a);
  end_process;
end arch;
ちょっとこれは複雑な記述です。 まず、VHDLでは、任意のビット数のXOR(排他的論理和)を求める 演算子がないので、関数br_xorを作っています。 XORを求める数値のビット数が決まっているのであれば、
x <= a(0) xor a(1) xor ...
のように羅列をしてもいいのですが、せっかくですので、 何ビットの数値でもXORを求められる関数br_xorをfunction以下で定義しています。 (ちなみに「--」以下はコメント) ここでは、配列aの全ビットの添え字(a'range)に対して、 それまでのXOR結果が入っているtmpと、当該ビットa(i)とのXORを 順番に求め、最後にtmpを返す、としています。

そして回路parityの実体のところでは、この関数br_xor()を呼んでいるだけ、 です。 このようにVHDLでは、「関数」としてfor文を書いても、 実際には1ビットずつXORを順番に求めていくのではなく、 あくまでも作られる論理回路としては、[p.78]の図のように XORゲートが並んでいて、一気に結果が求められる回路、となることに 注意しましょう。 (つまりクロックごとに1ビットずつXORを求める、という回路には 通常はなりません) つまり、あくまでもVHDLで記述しているのは、「回路の機能」であり、 「回路の実際の動作」ではない、ということに注意しておきましょう。


戻る