Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,13 @@ the wheel and publishes it to PyPI on a `v<version>` tag (version in
also reads it into `NAJA_SCHEMATIC_VERSION_STRING` for the C++ app's About
dialog, so a release is one edit plus a tag). For local work set
`NAJA_SCHEMATIC_BUNDLE` to a locally built bundle (see `_bundle.py`). Tests:
`pytest python/tests`.
`pytest python/tests`. `python/notebooks/colab_test.ipynb` is the
Open-in-Colab notebook (badge in the top `README.md`): a walkthrough of the
widget against the released PyPI package. `tests/test_notebook.py` runs it
in a real kernel (nbclient) and splices in a cell that plays the viewer
against its `view`, so a `Schematic`/protocol change that breaks it fails
CI; its install cell skips `pip install` when `naja_schematic` is already
importable, so CI tests the fresh wheel, not PyPI's.

## Architecture

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[![Native macOS Build](https://github.com/najaeda/naja-schematic/actions/workflows/native-macos.yml/badge.svg?branch=main)](https://github.com/najaeda/naja-schematic/actions/workflows/native-macos.yml)
[![Native Linux Build](https://github.com/najaeda/naja-schematic/actions/workflows/native-linux.yml/badge.svg?branch=main)](https://github.com/najaeda/naja-schematic/actions/workflows/native-linux.yml)
[![Emscripten Build](https://github.com/najaeda/naja-schematic/actions/workflows/emscripten.yml/badge.svg?branch=main)](https://github.com/najaeda/naja-schematic/actions/workflows/emscripten.yml)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/najaeda/naja-schematic/blob/main/python/notebooks/colab_test.ipynb)


> **Note:** This is a proof of concept in beta. Expect rough edges, breaking
Expand Down
2 changes: 2 additions & 0 deletions REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ path = [
"python/naja_schematic/static/index.html",
"python/naja_schematic/static/widget.js",
"python/naja_schematic/widget.py",
"python/notebooks/colab_test.ipynb",
"python/pyproject.toml",
"python/tests/conftest.py",
"python/tests/data/design.v",
"python/tests/test_notebook.py",
"python/tests/test_protocol.py",
"python/tests/test_transports.py",
"python/tests/test_widget.py",
Expand Down
3 changes: 3 additions & 0 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ pip install naja-schematic

## In a notebook (Jupyter, Google Colab, VSCode)

[Try it in Colab](https://colab.research.google.com/github/najaeda/naja-schematic/blob/main/python/notebooks/colab_test.ipynb):
a walkthrough of every widget feature below, runnable as is.

```python
from najaeda import netlist
import naja_schematic
Expand Down
272 changes: 272 additions & 0 deletions python/notebooks/colab_test.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "9a83c378",
"metadata": {},
"source": [
"# naja-schematic in Google Colab — test notebook\n",
"\n",
"Checks the `naja_schematic` widget end to end in Colab. Run cells top to bottom; each **Check:** line says what to look for."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d2afb533",
"metadata": {},
"outputs": [],
"source": [
"# Installs the released package (Colab); skipped where naja_schematic is\n",
"# already installed, e.g. when CI runs this notebook against a fresh wheel.\n",
"import importlib.util\n",
"if importlib.util.find_spec(\"naja_schematic\") is None:\n",
" %pip install -q naja-schematic\n",
"\n",
"import najaeda, naja_schematic, anywidget\n",
"print(\"naja-schematic\", naja_schematic.__version__, \"| anywidget\", anywidget.__version__)"
]
},
{
"cell_type": "markdown",
"id": "3fd7c1a6",
"metadata": {},
"source": [
"## 1. Small hierarchical design"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "36ae7db0",
"metadata": {},
"outputs": [],
"source": [
"%%writefile fulladder.v\n",
"module halfadder(input a, input b, output sum, output carry);\n",
" and carry_and(carry, a, b);\n",
" xor sum_xor(sum, a, b);\n",
"endmodule\n",
"\n",
"module fulladder(input a, input b, input cin, output sum, output cout);\n",
" wire sum1, carry1, carry2;\n",
" halfadder ha1(.a(a), .b(b), .sum(sum1), .carry(carry1));\n",
" halfadder ha2(.a(sum1), .b(cin), .sum(sum), .carry(carry2));\n",
" or cout_or(cout, carry1, carry2);\n",
"endmodule"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9bfd1fb5",
"metadata": {},
"outputs": [],
"source": [
"from najaeda import netlist\n",
"netlist.reset()\n",
"top = netlist.load_verilog(\"fulladder.v\")\n",
"top.get_name()"
]
},
{
"cell_type": "markdown",
"id": "45082e18",
"metadata": {},
"source": [
"## 2. Basic view\n",
"**Check:** viewer renders; tree shows `fulladder`; expanding `ha1`/`ha2` lazily loads instances, terms and nets; right-click a term → *Show Equipotential* / *Trace to Driver* draws a schematic; clicking an open pin grows it; *Show Properties* fills the Properties tab."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9cac3fc0",
"metadata": {},
"outputs": [],
"source": [
"view = naja_schematic.show(height=600)\n",
"view"
]
},
{
"cell_type": "markdown",
"id": "f9cbf15b",
"metadata": {},
"source": [
"## 3. Diagnosis overlay (push into an existing view)\n",
"**Check:** in the view above, `ha1` turns red in the tree and schematic, the `sum1` wire/pin is tinted, and the Diagnosis panel lists both items."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e81283ce",
"metadata": {},
"outputs": [],
"source": [
"view.annotate([\n",
" {\"kind\": \"instance\", \"path\": [\"ha1\"], \"severity\": \"error\",\n",
" \"message\": \"not equivalent\", \"source\": \"kepler-formal\"},\n",
" {\"kind\": \"net\", \"path\": [\"ha1\"], \"terminal\": \"sum\", \"severity\": \"warning\",\n",
" \"message\": \"suspicious output\", \"source\": \"colab-test\"},\n",
"])"
]
},
{
"cell_type": "markdown",
"id": "fa05dd8c",
"metadata": {},
"source": [
"## 4. Host → viewer: focus an instance\n",
"**Check:** tree opens down to `ha2/sum_xor`, it's selected, Properties shows it, schematic draws it alone with open pins."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2f5219d",
"metadata": {},
"outputs": [],
"source": [
"view.show_instance(\"ha2/sum_xor\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b40b4288",
"metadata": {},
"outputs": [],
"source": [
"# Same thing with a najaeda Instance\n",
"view.show_instance(top.get_child_instance(\"ha1\").get_child_instance(\"carry_and\"))"
]
},
{
"cell_type": "markdown",
"id": "2966c079",
"metadata": {},
"source": [
"## 5. Viewer → host: selection\n",
"Click an instance in the tree or a box in the schematic, then run the next cell. The callback prints on each click (output appears under this cell)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2125d57",
"metadata": {},
"outputs": [],
"source": [
"handler = view.on_select(lambda inst: print(\"selected:\", inst))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4ab2dcc9",
"metadata": {},
"outputs": [],
"source": [
"print(\"selected_path:\", view.selected_path)\n",
"inst = view.selected\n",
"print(inst, \"->\", inst.get_model_name() if inst else None)"
]
},
{
"cell_type": "markdown",
"id": "854f5c0f",
"metadata": {},
"source": [
"## 6. One-shot view with diagnosis + instance"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7b22e0f3",
"metadata": {},
"outputs": [],
"source": [
"naja_schematic.show(\n",
" height=500,\n",
" instance=\"ha1\",\n",
" diagnosis=[{\"kind\": \"instance\", \"path\": [\"ha2\"], \"severity\": \"info\",\n",
" \"message\": \"hello from show()\", \"source\": \"colab-test\"}],\n",
")"
]
},
{
"cell_type": "markdown",
"id": "5d91666e",
"metadata": {},
"source": [
"## 7. Edit, then a fresh view\n",
"**Check:** the new view shows `halfadder1` instead of `ha1` (the older view above does not refresh — known gap)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b86790fd",
"metadata": {},
"outputs": [],
"source": [
"top.get_child_instance(\"ha1\").set_name(\"halfadder1\")\n",
"naja_schematic.show(height=500)"
]
},
{
"cell_type": "markdown",
"id": "8a71293c",
"metadata": {},
"source": [
"## 8. Larger gate-level design with Liberty (tinyrocket)\n",
"**Check:** loads in reasonable time; tree scrolls; tracing a flop input to driver works and stays responsive."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ac9e690a",
"metadata": {},
"outputs": [],
"source": [
"!curl -sSfL https://github.com/najaeda/naja/archive/refs/heads/main.zip -o naja-main.zip && unzip -q -o naja-main.zip \"naja-main/tutorials/benchmarks/*\"\n",
"bench = \"naja-main/tutorials/benchmarks\"\n",
"netlist.reset()\n",
"netlist.load_liberty([f\"{bench}/liberty/tutorial_cells.lib\", f\"{bench}/liberty/fakeram45_64x32.lib\"])\n",
"top = netlist.load_verilog(f\"{bench}/verilog/tinyrocket/tinyrocket.v\")\n",
"naja_schematic.show(height=700)"
]
},
{
"cell_type": "markdown",
"id": "cbbaad03",
"metadata": {},
"source": [
"## Things to note while testing\n",
"- Mouse wheel over the viewer doesn't scroll the page (known).\n",
"- Two/three views on the page at once: do they all stay interactive?\n",
"- Keyboard input only goes to the focused viewer?\n",
"- Any errors in the browser console (F12) or the Colab cell output?"
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
3 changes: 2 additions & 1 deletion python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ classifiers = [
]

[project.optional-dependencies]
test = ["pytest"]
# nbformat/nbclient/ipykernel: tests/test_notebook.py runs the Colab notebook.
test = ["pytest", "nbformat", "nbclient", "ipykernel"]

[project.scripts]
naja-schematic = "naja_schematic.server:main"
Expand Down
Loading
Loading