codex/past-exam-of-the-year-1-of-the-computer-science-course-of-the-university-of-oxford/2015/digital-resit-2015-cmos-network.py

past-exam-of-the-year-1-of-the-computer-science-course-of-the-university-of-oxford/2015/digital-resit-2015-cmos-network.py
"""Draw the Q2(d) channel schematic into the current working directory.

Tested with Python 3.14.4 and matplotlib 3.10.7 (repository pyproject.toml).
Uses only matplotlib plus the standard library. Bodies are omitted; tie PMOS
bodies to VDD and NMOS bodies to ground. Four input inverters are not drawn.
"""

from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Circle


def main():
    plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 12})
    fig, ax = plt.subplots(figsize=(8, 10), dpi=140, facecolor="white")
    ax.set_facecolor("white")

    def wire(xs, ys):
        ax.plot(xs, ys, color="#263238", linewidth=1.5, solid_capstyle="round")

    def device(x, y, gate, kind):
        # Vertical channel, source/drain contacts and insulated gate.
        wire([x, x], [y - 0.5, y - 0.18])
        wire([x, x], [y - 0.18, y + 0.18])
        wire([x, x], [y + 0.18, y + 0.5])
        wire([x - 0.16, x - 0.16], [y - 0.16, y + 0.16])
        end = x - (0.29 if kind == "p" else 0.16)
        wire([x - 0.48, end], [y, y])
        if kind == "p":
            ax.add_patch(Circle((x - 0.225, y), 0.065, facecolor="white",
                                edgecolor="#263238", linewidth=1.3, zorder=3))
        ax.text(x - 0.56, y, gate, ha="right", va="center", fontsize=13)

    # Each PMOS branch consists of two parallel pairs in series.
    for center, labels in [(3.0, ("$a$", "$\\overline{c}$", "$\\overline{a}$", "$c$")),
                           (7.0, ("$b$", "$\\overline{d}$", "$\\overline{b}$", "$d$"))]:
        left, right = center - 0.65, center + 0.65
        for y, pair in [(8.0, labels[:2]), (6.5, labels[2:])]:
            for x, label in zip((left, right), pair):
                device(x, y, label, "p")
            wire([left, right], [y + 0.5, y + 0.5])
            wire([left, right], [y - 0.5, y - 0.5])
        wire([center, center], [8.5, 9.0])
        wire([center, center], [7.5, 7.0])
        wire([center, center], [6.0, 5.5])
    wire([3, 7], [9, 9])
    wire([5, 5], [9, 9.35])
    ax.text(5, 9.43, "$V_{DD}$", ha="center", va="bottom", fontsize=15)
    wire([3, 7], [5.5, 5.5])
    wire([5, 5], [5.5, 5.0])
    wire([5, 8.3], [5.0, 5.0])
    ax.add_patch(Circle((5, 5), 0.045, color="#263238"))
    ax.text(8.45, 5, "$z$", va="center", fontsize=17)

    # Each NMOS module has two parallel branches, each with two series devices.
    for top, gates in [(4.7, ("$a$", "$\\overline{c}$", "$\\overline{a}$", "$c$")),
                       (2.3, ("$b$", "$\\overline{d}$", "$\\overline{b}$", "$d$"))]:
        bottom = top - 2.0
        wire([3.5, 6.5], [top, top])
        wire([3.5, 6.5], [bottom, bottom])
        for x, pair in [(3.5, gates[:2]), (6.5, gates[2:])]:
            device(x, top - 0.5, pair[0], "n")
            device(x, top - 1.5, pair[1], "n")
    wire([5, 5], [5, 4.7])
    wire([5, 5], [2.7, 2.3])
    wire([5, 5], [0.3, -0.05])
    for y, half in [(-0.05, 0.35), (-0.17, 0.23), (-0.29, 0.10)]:
        wire([5 - half, 5 + half], [y, y])

    ax.text(0.1, 7.5, "Pull-up\n8 PMOS", ha="left", va="center", color="#235789")
    ax.text(0.1, 2.8, "Pull-down\n8 NMOS", ha="left", va="center", color="#235789")
    ax.set_title("Static CMOS: output high when either input pair agrees", fontsize=15, pad=20)
    ax.text(5, -0.8, "Gate bubble: PMOS conducts at 0.  NMOS conducts at 1.\n"
            "Add four input inverters: 16 network devices + 8 = 24 transistors.",
            ha="center", va="center", fontsize=11)
    ax.set_xlim(-0.2, 9.5)
    ax.set_ylim(-1.2, 10)
    ax.axis("off")
    fig.subplots_adjust(left=0.03, right=0.98, bottom=0.03, top=0.94)
    fig.savefig(Path.cwd() / "digital-resit-2015-cmos-network.png", facecolor="white")
    plt.close(fig)


if __name__ == "__main__":
    main()