You Deserve Nice Circuit Diagrams: Blockpic Draws Your Netlist

#FPGA#bazel#openxc7#blockpic#documentation#yosys#verilog#LaTeX

“You deserve nice circuit diagrams!”

Every digital design document needs pictures. A clean block diagram communicates dataflow, bus widths, and control hierarchy in seconds. Reading through hundreds of lines of RTL or dense prose takes much longer.

Yet in almost every hardware project, documentation suffers from the same problem. Diagrams are drawn once in a graphical editor, exported as static bitmaps, and checked in. Over time, register stages are refactored, control signals are renamed, and bus widths change. The diagram remains frozen in time, silently misleading anyone who reads it.

Blockpic takes a fresh approach. Designed specifically for engineering documentation and written in Go, Blockpic lets you treat digital circuit diagrams as code. Through integration with rules_openxc7, Blockpic can also draw gate-level schematics directly from the synthesized Yosys netlist. In a unified Bazel build graph, your RTL, synthesis, diagram rendering, and LaTeX publication are tied together. When the HDL changes, the figures update automatically.

The Philosophy of Blockpic

Blockpic is an open-source tool licensed under Apache-2.0. It generates digital schematics with the clean, legible aesthetic of AMD’s classic UG474 7 Series FPGAs Configurable Logic Block User Guide.

Key design principles of Blockpic include:

  • UG474-Style Aesthetics: Clean, sans-serif typography (Helvetica or DejaVu Sans), crisp orthogonal routing without distracting arrowheads, and pin names displayed inside component boxes.
  • Automated Placement and Routing: You define blocks, ports, and connections textually. Blockpic places and routes them without requiring manual coordinate tweaking.
  • Bus-Width Notation: Thick multi-bit bus traces crossed by short slashes indicating bit widths (e.g. 4) or SystemVerilog typedefs (such as nibble_t via Yosys’s wiretype attribute).
  • Hermetic Bazel Macros: Macros (blockpic_diagram and blockpic_netlist_diagram) allow diagrams to be compiled into SVGs inside automated CI/CD pipelines.

Two Complementary Views: Architectural Intent vs. Synthesized Reality

To see how Blockpic works, consider a 4-bit LED counter with synchronous reset (samples/blockpic-in-docs/counter.v):

// SPDX-License-Identifier: Apache-2.0
// A 4-bit counter shown on four LEDs: small enough to draw, with a bus
// that synthesis splits into bits.
module counter (
    input  wire       clk,
    input  wire       reset,
    output reg  [3:0] led
);
    always @(posedge clk) begin
        if (reset)
            led <= 4'b0;
        else
            led <= led + 4'b1;
    end
endmodule

A hardware engineer needs two distinct diagrams to understand this design:

  1. The Architectural Diagram: An abstraction of what the design intends to do.
  2. The Netlist Schematic: An exact picture of what the synthesis tools actually built.

Blockpic addresses both.

1. Drawing Architectural Intent by Hand (.blockpic)

For high-level architecture, you write a concise .blockpic description stored right beside your Verilog code. Here is samples/blockpic-in-docs/counter.blockpic:

// SPDX-License-Identifier: Apache-2.0
// The LED counter of counter.v, drawn by hand: a 4-bit register that
// adds one on every clock edge.
diagram LedCounter {
    block clk { kind: "INPUT_PIN"; outputs: out; }
    block reset { kind: "INPUT_PIN"; outputs: out; }
    block one { kind: "INPUT_PIN"; outputs: out; }
    block zero { kind: "INPUT_PIN"; outputs: out; }
    block plus {
        kind: "ADD"; name: "+1";
        inputs: A, B; outputs: Y;
    }
    block clear {
        kind: "MUX"; name: "clear";
        inputs: A, B, S; outputs: Y;
    }
    block count {
        kind: "DFF"; name: "count";
        inputs: D, CLK; outputs: Q;
    }
    block led { kind: "OUTPUT_PIN"; inputs: in; }

    wire count.Q -> plus.A { thickness: 4; }
    wire one.out -> plus.B { thickness: 4; }
    wire plus.Y -> clear.A { thickness: 4; }
    wire zero.out -> clear.B { thickness: 4; }
    wire reset.out -> clear.S
    wire clear.Y -> count.D { thickness: 4; }
    wire clk.out -> count.CLK
    wire count.Q -> led.in { thickness: 4; }

    group "counter" { plus; clear; count }
}

Blockpic maps component pins by their defined positions:

  • For DFF, input pins are D followed by clock CLK.
  • For MUX, data inputs A and B are followed by select S.
  • Thick wires represent buses, rendered with short slashes indicating bus widths.

When compiled with blockpic_diagram, Blockpic renders this clean vector diagram:

4-Bit Counter Architecture Diagram

2. Drawing What the FPGA Actually Built (blockpic_netlist_diagram)

Hand-drawn diagrams illustrate design intent. Hardware bugs, however, frequently occur when synthesis maps RTL into physical silicon primitives in unexpected ways.

Through rules_openxc7, Bazel runs Yosys (synth_xilinx) targeting AMD 7-series silicon (such as an xc7a200t or xc7a100t Artix-7). Yosys outputs an intermediate JSON netlist detailing every primitive cell, pin, and net. Blockpic’s blockpic import tool ingests this JSON and transforms it into a schematic:

Synthesized 4-Bit Counter Netlist Diagram

Examining the synthesized netlist reveals details absent from the architectural sketch:

  • Primitive Mapping: The 4-bit register is mapped directly into four FDRE flip-flops with synchronous reset (R) and clock enable (CE tied to VCC).
  • Carry Logic: Rather than a generic adder, Yosys instantiated a hardware CARRY4 primitive from the 7-series slice. Bit 0 does not use the carry chain; an inverter (INV) toggles it directly.
  • I/O Buffering: The clock and reset signals route through dedicated input buffers (IBUF) and a global clock buffer (BUFG).
  • Bus Splitting: The led bus is split into one pin per bit (led_0 through led_3), because each bit leaves through its own OBUF driver.

The Unified Build: From HDL to Publication in One Graph

Because Blockpic provides Bazel macros, your design files, synthesis runs, and document generation can all live in a single directed acyclic graph (DAG).

In samples/blockpic-in-docs/BUILD.bazel, the sample paper is assembled hermetically:

load("@rules_latex_host//latex:defs.bzl", "latex_document")
load("@rules_openxc7//openxc7:defs.bzl", "vivado_project", "vivado_synthesis")
load("//:defs.bzl", "blockpic_diagram", "blockpic_netlist_diagram")
load("//samples:defs.bzl", "svg_to_pdf")

# 1. Architectural diagram from .blockpic source
blockpic_diagram(
    name = "architecture",
    src = "counter.blockpic",
    out = "architecture.svg",
)

# 2. OpenXC7 synthesis to JSON netlist
vivado_project(
    name = "counter",
    srcs = ["counter.v"],
    part = "xc7a200tfbg484-2",
    top_level = "counter",
)

vivado_synthesis(
    name = "counter_synth",
    project = ":counter",
)

# 3. Automated netlist extraction from Yosys synthesis
blockpic_netlist_diagram(
    name = "netlist",
    netlist = ":counter_synth",
    out = "netlist.svg",
)

# 4. Convert vector SVGs to print-ready PDFs with embedded fonts
svg_to_pdf(
    name = "architecture_pdf",
    src = ":architecture",
    out = "architecture.pdf",
)

svg_to_pdf(
    name = "netlist_pdf",
    src = ":netlist",
    out = "netlist.pdf",
)

# 5. Typeset the final IEEEtran publication
latex_document(
    name = "blockpic_in_docs",
    data = [
        "counter.blockpic",
        "counter.v",
        ":architecture_pdf",
        ":netlist_pdf",
    ],
    main = "blockpic_in_docs.tex",
    visibility = ["//samples:__pkg__"],
    deps = glob(["sections/*.tex"]),
)

Note: The snippet above runs inside the Blockpic repository itself, using repo-relative loads //:defs.bzl and //samples:defs.bzl. With the release of Blockpic v2.0.1 (available in filmil/bazel-registry and submitted to the Bazel Central Registry), your own module can load @blockpic//:defs.bzl directly after adding bazel_dep(name = "blockpic", version = "2.0.1") to MODULE.bazel. blockpic_diagram and blockpic_netlist_diagram produce SVGs, while svg_to_pdf is a sample helper wrapping Node.js to produce PDFs with embedded DejaVu Sans fonts. Custom skins are located under @blockpic//skins: (such as @blockpic//skins:skin_xilinx.svg).

With this pipeline:

  • If you modify counter.v, running bazel build //... triggers synthesis, redraws netlist.svg, converts it to PDF, and re-compiles the LaTeX document.
  • If nothing has changed, Bazel’s action cache returns the entire paper in milliseconds.
  • The documentation cannot drift from the physical implementation.

The Showcase Paper

To demonstrate this workflow in practice, the repository includes a complete 3-page IEEE-style paper titled “Figures That Follow the Design: Drawing Block Diagrams and Netlists with Blockpic in a Bazel Build”.

The paper is compiled in CI and published directly on GitHub Releases:

Credits and Ecosystem

Blockpic builds on several foundational open-source hardware projects:

  • Yosys by Claire Wolf: The industry-standard open synthesis suite mapping RTL to hardware primitives.
  • netlistsvg by Neil Turley: The SVG layout engine converting Yosys netlists into visual graphs.
  • openXC7 & nextpnr: The open-source toolchain and place-and-route engine for AMD 7-series FPGAs.
  • Project X-Ray: The database documenting the 7-series bitstream architecture.
  • rules_openxc7: Bazel rules for AMD 7-series FPGA development.

Try Blockpic Today

Blockpic is ready to bring automated, reproducible diagramming to your FPGA and ASIC projects. Clone the repository, try compiling your own diagrams, and integrate them into your technical notes, whitepapers, or specification sheets: