LaTeX で論理回路 (1)

この記事では「論理回路」に限って扱います。

\LaTeX で論理回路を描くのは、まぁひかえめに言っても苦行なのですが、それはさておきます。

代表的な方法

私が知る限り現在の代表的な方法としては以下の3種類があります。

TikZ の circuits.logic ライブラリ

長所として、

  • TikZ で閉じている

のですが、

  • 記号をはみだすような多入力 (下図参照) が (おそらく) 描けない
  • 用意されている部品が限られている (たぶんフリップフロップとかはない)

という欠点もあります。

CircuiTikZ

なんとなく良さげな名前ですが、論理回路に限っていえば利点が見当りません…
欠点としては、

  • American style の記号が正しくない (MIL/ANSI でも DIN でもない)
  • 三入力までしか描けない?

で、全く美しくない、というのが正直な感想です。

Circuit macros

  • かなり柔軟
  • TikZ/PSTricks/PDF などいろいろ出力できる

で非常に良い感じなのですが、

  • M4 マクロなので、その記法を覚える必要あり
  • M4 マクロから変換の手間が必要

と、習得のコストが大きめです。

なお、以前は TeX Live に含まれていませんでしたが、TeX Live 2019 以降で追加されたようです。

それぞれのサンプル

試しにそれぞれで Full adder を書いてみましたので最後に付けます。
TikZ/circuits.logic と CircuiTikZ はかなり似ているので、一方から他方への書き換えは比較的楽です。

見ての通り、TikZ/circuits.logic と Circuit macros はかなり綺麗な出力が得られますが、CircuiTikZ はちょっとアレすぎます。ということで、私的には論理回路を描くのに CircuiTikZ は完全にナシです。

大きめの回路では Circuit macros を使うのが現時点ではベストではないでしょうか (Full adder くらいの規模だとちょっと悩む)。

なお、Circuit macros には、

  • Cirkuit (http://wwwu.uni-klu.ac.at/magostin/cirkuit.html)
  • PyCirkuit (https://pypi.org/project/pycirkuit/)

なるフロントエンドがあるようですが、私自身は試してません。

TikZ/circuits.logic と CircuiTikZ はもうちょっと綺麗なソースコードで書けそうな気もしますが、使う予定がないのであまり追求してません。

TikZ/circuits.logic

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{circuits.logic.US}

\begin{document}

% http://tex.stackexchange.com/questions/32839/drawing-circuit-diagrams-with-logic-gates-in-latex
\tikzstyle{branch}=[fill,shape=circle,minimum size=3pt,inner sep=0pt]

\begin{center}
  \begin{tikzpicture}[circuit logic US]
    % Half adder 2
    \draw (4,1) node[xor gate] (xor2) {};
    \draw (xor2)+(0,1) node[and gate] (and2) {};

    % Half adder 1
    \draw (and2.input 1)+(-2,0) node[xor gate] (xor1) {};
    \draw (xor1)+(0,1) node[and gate] (and1) {};

    % Carry
    \draw ($0.5*(and1)+0.5*(and2)+(3,0)$) node[or gate] (or) {};

    % connection between AND1 and XOR1
    \draw (and1.input 1) to ++(-0.70,0) node [branch] {} |- (xor1.input 1);
    \draw (and1.input 2) to ++(-0.35,0) node [branch] {} |- (xor1.input 2);

    % connection between HA1 and HA2
    \draw (xor1.output) -- (and2.input 1);

    % connection between AND1 and XOR1
    \draw (xor1.output)  to ++( 0.70,0) node [branch] {} |- (xor2.input 1);
    \draw (xor2.input 2) to ++(-0.35,0) node [branch] {} |- (and2.input 2);

    % connection between AND1,2 and OR
    \draw (or.input 2) -- ++(-0.4,0) |- (and2.output);
    \draw (or.input 1) -- ++(-0.4,0) |- (and1.output);

    % outputs
     \draw (or.output) -- ++(1,0) node[right] (output) {$c_n$};
     \draw (xor2.output) -- ($($(or.output)+(1,0)$)!(xor2.output)!($(or.output)+(1,1)$)$) node[right] {$s_n$};

    % inputs
     \draw (xor2.input 2) -- ($(0,1)!(xor2.input 2)!(0,0)$) node[left] {$c_{n-1}$};
     \draw (and1.input 1) -- ($(0,1)!(and1.input 1)!(0,0)$) node[left] {$a_n$};
     \draw (and1.input 2) -- ($(0,1)!(and1.input 2)!(0,0)$) node[left] {$b_n$};
  \end{tikzpicture}
\end{center} 

\end{document}

CircuiTikZ

\documentclass{article}
\usepackage[americanports]{circuitikz}
\begin{document}

\begin{center}
  \begin{circuitikz}
    % Half adder 2
    \draw (7,1) node[xor port] (xor2) {};
    \draw (xor2)+(0,2) node[and port] (and2) {};

    % Half adder 1
    \draw (and2.in 1)+(-2,0) node[xor port] (xor1) {};
    \draw (xor1)+(0,2) node[and port] (and1) {};

    % Carry
    \draw ($0.5*(and1)+0.5*(and2)+(4,0)$) node[or port] (or) {};

    % connection between AND1 and XOR1
    \draw (and1.in 1) to [short,-*] ++(-0.70,0) |- (xor1.in 1);
    \draw (and1.in 2) to [short,-*] ++(-0.35,0) |- (xor1.in 2);

    % connection between HA1 and HA2
    \draw (xor1.out) -- (and2.in 1);

    % connection between AND1 and XOR1
    \draw (xor1.out)  to [short,-*] ++( 0.70,0) |- (xor2.in 1);
    \draw (xor2.in 2) to [short,-*] ++(-0.35,0) |- (and2.in 2);

    % connection between AND1,2 and OR
    \draw (and2.out) -| (or.in 2);
    \draw (and1.out) -| (or.in 1);

    % outputs
    \draw (or.out) -- ++(1,0) node[right] (out) {$c_n$};
    \draw (xor2.out) -- ($($(or.out)+(1,0)$)!(xor2.out)!($(or.out)+(1,1)$)$) node[right] {$s_n$};

    % inputs
    \draw (xor2.in 2) -- ($(0,1)!(xor2.in 2)!(0,0)$) node[left] {$c_{n-1}$};
    \draw (and1.in 1) -- ($(0,1)!(and1.in 1)!(0,0)$) node[left] {$a_n$};
    \draw (and1.in 2) -- ($(0,1)!(and1.in 2)!(0,0)$) node[left] {$b_n$};
  \end{circuitikz}
\end{center} 

\end{document}

Circuit macros

(図では線の太さにバラつきがありますが、レンダリングの問題で、実際には同じ太さで描かれています。)

dnl fadder.m4

.PS

log_init

AND1: AND_gate
line left AND_wd*2*L_unit from AND1.In1; "$a_n$" rjust
line left AND_wd*2*L_unit from AND1.In2; "$b_n$" rjust

XOR1: XOR_gate at AND1-(0,AND_ht*2)*L_unit
line left AND_wd/2*L_unit from XOR1.In1 \
    then up AND_ht*2*L_unit ;  dot
line left AND_wd*L_unit from XOR1.In2 \
    then up AND_ht*2*L_unit ; dot

AND2: AND_gate at XOR1+(AND_wd*2.5,-AND_ht/4)*L_unit
line from XOR1.Out to AND2.In1

OR1: OR_gate at AND2+(AND_wd*2,AND_ht)*L_unit
line left 0.5*AND_wd*L_unit from OR1.In1
line to (Here.x, AND1.Out.y) then to AND1.Out

line left 0.5*AND_wd*L_unit from OR1.In2
line to (Here.x, AND2.Out.y) then to AND2.Out

line right AND_wd*L_unit from OR1.Out; "$c_n$" ljust

XOR2: XOR_gate at AND2+(0,-AND_ht*2)*L_unit
line left AND_wd/2*L_unit from AND2.In2
line to (Here.x, XOR2.In2.y); dot

line from XOR2.In2 to (AND1.In1.x - AND_wd*2*L_unit, XOR2.In2.y)
"$c_{n-1}$" rjust

line left AND_wd*L_unit from XOR2.In1
line to (Here.x, AND2.In1.y); dot

line from XOR2.Out to (OR1.Out.x + AND_wd*L_unit, XOR2.Out.y); "$s_n$" ljust

.PE
% example.tex
\documentclass{article}

\usepackage{tikz}

\begin{document}

\begin{center}
  % m4 -I /usr/local/share/Circuit_macros liblog.m4 pgf.m4 fadder.m4 > fadder.pic
  % dpic -g fadder.pic > fadder.tex
  \input{fadder}
\end{center}

\end{document}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です