You Deserve Nice Circuit Diagrams: Blockpic Draws Your Netlist
“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 asnibble_tvia Yosys’swiretypeattribute). - Hermetic Bazel Macros: Macros (
blockpic_diagramandblockpic_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:
- The Architectural Diagram: An abstraction of what the design intends to do.
- 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 areDfollowed by clockCLK. - For
MUX, data inputsAandBare followed by selectS. - Thick wires represent buses, rendered with short slashes indicating bus widths.
When compiled with blockpic_diagram, Blockpic renders this clean vector 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:
Examining the synthesized netlist reveals details absent from the architectural sketch:
- Primitive Mapping: The 4-bit register is mapped directly into four
FDREflip-flops with synchronous reset (R) and clock enable (CEtied toVCC). - Carry Logic: Rather than a generic adder, Yosys instantiated a hardware
CARRY4primitive 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
ledbus is split into one pin per bit (led_0throughled_3), because each bit leaves through its ownOBUFdriver.
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.bzland//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.bzldirectly after addingbazel_dep(name = "blockpic", version = "2.0.1")toMODULE.bazel.blockpic_diagramandblockpic_netlist_diagramproduce SVGs, whilesvg_to_pdfis 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, runningbazel build //...triggers synthesis, redrawsnetlist.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:
- Download the sample PDF: blockpic_in_docs.pdf on GitHub Releases
- Inspect the sources: samples/blockpic-in-docs
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:
- Repository: https://github.com/filmil/blockpic
- Tagline: You deserve nice circuit diagrams!
- License: Apache-2.0