002回
簡単な論理回路・1

key word :
AND , OR , Ex-OR , NOT , Half-Adder , Full-Adder
下位モジュールとの接続方法(順番・名前) 7セグメントデコーダー


001の方では大まかな構成を書いたので、今回は論理回路の記述を。


・AND
Verilog 記述 真理値表 記号

module name(a,b,z);

 input a,b;
 output z;
 assign z = a & b;

end module
abz
000
010
100
111



あと、ここで説明することとしたら [X:0] の記述。
これは信号のビット長を定めるもので、8bitの信号を扱いたい場合は[7:0]って記述するのが一般的です。(7 downto 0)
一応[0:7]とか[8:1]って書いても大丈夫なんですけど、最下位bitが0bit目ってのが一番慣れ親しんでると思うんで、[7:0] で進めていきます。

[7:0] ↓ こんなイメージ
7bit6bit5bit4bit 3bit2bit1bit0bit



・OR
Verilog 記述 真理値表 記号

module name(a,b,z);

 input a,b;
 output z;
 assign z = a | b;

end module
abz
000
011
101
111



OR。それ以上でもそれ以下でもない。



・Ex-OR
Verilog 記述 真理値表 記号

module name(a,b,z);

 input a,b;
 output z;
 assign z = a ^ b;

end module
abz
000
011
101
110



あんまり使わないから、久しぶりに使うときどっちが1だったか忘れてしまったり。



・Not
Verilog 記述 真理値表 記号

module name(a,b,z);

 input a;
 output z;
 assign z = ~a;

end module
az
01
10



否定。



とりあえず最低限の記述はこんなもんです。



・半加算器 Half-Adder
Verilog 記述 真理値表 記号

input x,y;
output z,c;
assign z = x ^ y;
assign c = x & y;

xyzc
0000
0110
1010
1101



半加算。
入力した2つの値を加算し、その出力と桁上がり分を出力

真理値表を見ての通り、桁上がり無しの出力zはEx-ORの値。
桁上がりの分はANDの値。




・全加算器 Full-Adder
Verilog 記述 内容

module HA(x,y,z,c);
input x,y;
output z,c;
assign z = x ^ y;
assign c = x & y;
endmodule



input x,y,Cin;
output Zout,Cout;
wire z1,c1,c2

HA HA_1(x,y,z1,c1);
HA HA_2(.x(z1),.y(Cin),.z(Zout),.c(c2));
assign Cout = c2 ^ c1;

全加算。
半加算器の入力に、桁上がり分を追加

Haを組み合わせて記述。

Verilog記述の上部に記述してあるのHAがモジュール名で、作った各.vファイルの最初に定めたmodule名と同じ。 別のファイルで作ってある。
一応、同一のファイル内に記述して後で呼び出してもRTLシミュでは動くが、 ゲートシミュで使うツールでは駄目な場合があるのでモジュール毎に分けるのが吉

下部でのHA_1,HA_2は、インスタンス名で、このモジュール内で使用する際の個別の名前
functionが回路内部に記述するのに対し、外部にある回路といったイメージである。

HA_1では、ポートリスト順による接続をしており、モジュールHAに記述されている順に値が代入される。
使用する値が少なく、構造も簡単な場合は手間がかからず楽であるが、
信号数が増え、接続が複雑になるとHA_2の様な名前による接続を行ったほうが 見ただけで判るので良い。 他人に判り易く、後から記述を見ても判り易いという事を考えると、後者の方が優れている。
真理値表 記号
Cinxyz1c1Zoutc2Cout
0 0000000
0 0110100
0 1010100
0 1101001
1 0000100
1 0110011
1 1010011
1 1101101


・下位モジュールとの接続に関して

module HA(x,y,z,c);
input a,b;
output z,c;
assign z = x ^ y;
assign c = x & y;
endmodule

↑呼び出し対象

・順番による接続
HA HA_1(x,y,z1,c1);
名前の通り、呼び出し側モジュールで値が宣言されている順にならって値を代入。
xにはxを yにはyを zにはz1を cにはc1を入れる。
HA HA_1(z1,c1,x,y);
逆に↑の様に記述すれば
xにはz1を yにはc1を zにはxを cにはyが代入される。

・名前による接続
HA HA_2(.x(z1),.y(Cin),.z(Zout),.c(c2));
同じく名前の通り、呼び出し対象のどの部分にどんな値を入れるかを記述。
.x( )ってのが、呼び出し対象側のxに相当。
最初はちょっと面倒に感じるかもしれないけれど、視覚的に判り易いのでこちらで慣れるのをお勧めします。




・7セグメントデコーダ
動作 真理値表 記号

a-gの信号がLの時に点灯。
10進ABCDabcdefg16進
00000000000101
1000110011114F
20010001001012
30011000011006
4010010011004C
50101010010024
60110010000020
7011100011010D
81000000000000
9100100011000C



時間の入力信号qに対してそれをデコードし、入力信号に応じたデコード信号a-gを出力する。
真理値表より、 a が点灯する条件としては入力値A-Dが"0001"と"0100"の時であり、これを回路で表すと以下のようになる。



そして、Verilog記述は


assign a = (~A & ~B & ~C & D) | (~A & B & ~C & ~D);


となる
他のも同様に記述すればよいのだが、如何せん面倒なのでカルノー図を使ってちょっくら楽をする↓



assign e = (~A & D) | (~A & ~B & ~C) | (~B & ~C & D);

更に冗長項を利用すると
assign e = D | (~B & ~C);

となる。

今更カルノー図の説明は要らんよね?



module segment(q,a,b,c,d,e,f,g);
input [3:0] q;
output a,b,c,d,e,f,g;

assign a = (~q[3] & ~q[2] & ~q[1] & q[0]) | (~q[3] & q[2] & ~q[1] & ~q[0]);
assign b = (~q[3] & q[2] & ~q[1] & q[0]) | (~q[3] & q[2] & q[1] & ~q[0]);
assign c = (~q[3] & ~q[2] & q[1] & ~q[0]);
assign d = (~q[2] & ~q[1] & q[0]) | (~q[3] & q[2] & ~q[1] & ~q[0]) | (~q[3] & q[2] & q[1] & q[0]);
assign e = (~q[3] & q[0]) | (q[3] & ~q[2] & ~q[1] & q[0]) | (~q[3] & q[2] & ~q[1] & ~q[0]);
assign f = (~q[3] & ~q[2] & q[1]) | (~q[3] & ~q[2] & q[0]);
assign g = (~q[3] & ~q[2] & ~q[1]) | (~q[3] & q[2] & q[1] & q[0]);

endmodule



module segment(q,seg);
input [3:0] q;
output [6:0] seg;

assign seg[6] = (~q[3] & ~q[2] & ~q[1] & q[0]) | (~q[3] & q[2] & ~q[1] & ~q[0]);
assign seg[5] = (~q[3] & q[2] & ~q[1] & q[0]) | (~q[3] & q[2] & q[1] & ~q[0]);
assign seg[4] = (~q[3] & ~q[2] & q[1] & ~q[0]);
assign seg[3] = (~q[2] & ~q[1] & q[0]) | (~q[3] & q[2] & ~q[1] & ~q[0]) | (~q[3] & q[2] & q[1] & q[0]);
assign seg[2] = (~q[3] & q[0]) | (q[3] & ~q[2] & ~q[1] & q[0]) | (~q[3] & q[2] & ~q[1] & ~q[0]);
assign seg[1] = (~q[3] & ~q[2] & q[1]) | (~q[3] & ~q[2] & q[0]);
assign seg[0] = (~q[3] & ~q[2] & ~q[1]) | (~q[3] & q[2] & q[1] & q[0]);

endmodule

そんな訳でカルノー図を使い記述を楽にするのだけれど、それでも結構面倒だし、 第一式を見ただけじゃ何をやっているのか分からんです。
一応動きますけど。。

また、下の例のように配列に値を収めても構わないです。


module segment(q,segment);
input [3:0] q;
output [6:0] segment;

reg [6:0]segment;

always @(q)begin
 case({q})
  4'b0000 : segment = 7'b0000001;
  4'b0001 : segment = 7'b1001111;
  4'b0010 : segment = 7'b0010010;
  4'b0011 : segment = 7'b0000110;
  4'b0100 : segment = 7'b1001100;
  4'b0101 : segment = 7'b0100100;
  4'b0110 : segment = 7'b0100000;
  4'b0111 : segment = 7'b0001101;
  4'b1000 : segment = 7'b0000000;
  4'b1001 : segment = 7'b0001100;
  default : segment = 7'b0000000;

 endcase
end

endmodule

そこで登場するのが case文
qの値が 4'b000 だった場合 segment = 7'0000001;を行う
って感じです。
予期しない値が入力された場合に備えてdefaultを定めるのはデフォ。
この場合のalwaysは、qが変化した際に動作するって意味。


ん〜、まだまだ書くことがあるけれど何だか長くなったので次回へー。


[<<前へ | ↑Topへ戻る↑ | 次へ>>]