pcbjam/docs/IMPLEMENTATION_PLAN.md
Viktor Vaczi 2d7c26feac Initial project setup for KiCad WebAssembly port
- Add KiCad fork as git submodule
- Create project structure (patches, stubs, cmake, scripts, core, docs)
- Add knowledge base and implementation plan documentation
- Add README with project overview and goals

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 10:46:44 +01:00

8.2 KiB

KiCad Wasm Port - Implementation Plan

Goal

Run KiCad with native wxWidgets GUI, but with core logic (file parsing, geometry, DRC, routing) executing in a WebAssembly module. This validates the Wasm build before tackling the browser UI.


Phase 1: Minimal Build with Stubs

Objective: Get KiCad compiling with optional deps disabled via preprocessor guards.

1.1 Create Project Structure

kicad-wasm/
├── kicad/                    # Git submodule → upstream KiCad
├── patches/
│   └── 0001-optional-deps.patch
├── stubs/
│   └── include/
│       ├── git2.h            # Minimal stub headers
│       └── curl/curl.h
├── cmake/
│   └── KicadWasmOptions.cmake
├── CMakeLists.txt
└── scripts/
    ├── prepare.sh            # Apply patches
    └── update-kicad.sh       # Update to new KiCad version

1.2 Create the Patch

Add CMake options to CMakeLists.txt (after line 107):

option( KICAD_USE_CURL "Enable network features" ON )
option( KICAD_USE_GIT "Enable git integration" ON )
option( KICAD_USE_OCC "Enable OpenCASCADE for STEP" ON )
option( KICAD_USE_NGSPICE "Enable SPICE simulation" ON )
option( KICAD_USE_DATABASE "Enable database libraries" ON )

Wrap find_package calls conditionally:

if( KICAD_USE_CURL )
    find_package( CURL REQUIRED )
    add_compile_definitions( KICAD_USE_CURL )
else()
    add_library( CURL::libcurl INTERFACE IMPORTED )
endif()

1.3 Create Stub Headers

Minimal headers that let code compile without the actual libraries:

// stubs/include/git2.h
#pragma once
typedef struct git_repository git_repository;
// Stub functions return error codes

1.4 Add Preprocessor Guards to Source

Wrap implementations in common/git/*.cpp, common/kicad_curl/*.cpp:

#ifdef KICAD_USE_GIT
// actual implementation
#else
// return error or throw "feature disabled"
#endif

1.5 Verify Native Build

cd kicad-wasm
./scripts/prepare.sh
mkdir build && cd build
cmake .. -DKICAD_USE_CURL=OFF -DKICAD_USE_GIT=OFF -DKICAD_USE_OCC=OFF
make -j$(nproc)

Confirm KiCad launches and can open/edit PCB files (without git/network/STEP features).


Phase 2: Extract Core Library

Objective: Build core computation code as a standalone library that can be compiled to both native and Wasm.

2.1 Identify Core Components

Create kicad-wasm/core/CMakeLists.txt targeting:

Component Source Location Notes
Math/Geometry libs/kimath/ Pure C++, no deps
Core utilities libs/core/ Pure C++
KiCad file parser common/io/kicad/ Needs minimal deps
S-expression parser libs/sexpr/ Pure C++
Board data model pcbnew/board*.cpp Extract carefully

2.2 Define API Boundary

Create a C API for the core (easier Wasm interop than C++):

// kicad-wasm/core/include/kicad_core_api.h
extern "C" {
    // File operations
    void* kicad_load_pcb(const char* data, size_t len);
    void kicad_free_pcb(void* board);
    char* kicad_serialize_pcb(void* board);

    // Query
    int kicad_get_track_count(void* board);

    // Modification
    void kicad_add_track(void* board, /* params */);
}

2.3 Build Core as Static Library

# kicad-wasm/core/CMakeLists.txt
add_library(kicad_core STATIC
    ${KIMATH_SOURCES}
    ${SEXPR_SOURCES}
    ${IO_KICAD_SOURCES}
    api/kicad_core_api.cpp
)

target_compile_definitions(kicad_core PRIVATE
    KICAD_CORE_ONLY=1
)

Phase 3: Compile Core to WebAssembly

Objective: Build kicad_core.wasm using Emscripten.

3.1 Emscripten Build

source /path/to/emsdk/emsdk_env.sh

cd kicad-wasm
mkdir build-wasm && cd build-wasm

emcmake cmake ../core \
    -DCMAKE_BUILD_TYPE=Release \
    -DKICAD_WASM_BUILD=ON

emmake make

3.2 Export Functions

# For Emscripten
if(EMSCRIPTEN)
    set_target_properties(kicad_core PROPERTIES
        LINK_FLAGS "-s EXPORTED_FUNCTIONS='[_kicad_load_pcb,_kicad_free_pcb,...]' \
                    -s EXPORTED_RUNTIME_METHODS='[ccall,cwrap]' \
                    -s MODULARIZE=1 \
                    -s EXPORT_NAME='KicadCore'"
    )
endif()

3.3 Test Wasm Module

Create a simple test that loads a .kicad_pcb file:

// test/test_core.mjs
import KicadCore from './kicad_core.js';

const core = await KicadCore();
const pcbData = fs.readFileSync('test.kicad_pcb', 'utf8');
const board = core.ccall('kicad_load_pcb', 'number', ['string', 'number'],
                          [pcbData, pcbData.length]);
console.log('Track count:', core.ccall('kicad_get_track_count', 'number', ['number'], [board]));

Phase 4: Native App with Wasm Worker

Objective: Run native KiCad GUI but delegate core operations to the Wasm module.

4.1 Choose Wasm Runtime

Options for running Wasm in native app:

  • Wasmtime - Rust-based, mature, good C API
  • wasm3 - Small, fast interpreter
  • WAMR - WebAssembly Micro Runtime, lightweight

Recommend: Wasmtime for development (better debugging), wasm3 for size.

4.2 Create Wasm Bridge

// kicad-wasm/bridge/wasm_bridge.h
class WasmBridge {
public:
    WasmBridge(const std::string& wasmPath);

    BOARD* LoadPCB(const std::string& data);
    std::string SerializePCB(BOARD* board);
    void RunDRC(BOARD* board);

private:
    wasmtime_instance_t* m_instance;
};

4.3 Integrate with KiCad

Modify KiCad to optionally use the Wasm bridge:

// In pcbnew loading code
#ifdef USE_WASM_CORE
    auto board = WasmBridge::Get().LoadPCB(fileContent);
#else
    auto board = IO_KICAD::Load(filename);
#endif

4.4 Validate Correctness

  1. Load same PCB file via native code and via Wasm bridge
  2. Compare serialized output (should be identical)
  3. Run DRC via both paths, compare results
  4. Profile performance difference

Phase 5: Incremental Migration

Objective: Move more functionality to Wasm core, validate stability.

Priority Order

  1. File I/O - parsing and serialization
  2. DRC engine - computationally intensive, isolated
  3. Router - Push & Shove algorithms
  4. ERC - schematic checks

Validation Strategy

For each migrated component:

  1. Keep both native and Wasm implementations
  2. Add flag to switch between them
  3. Run test suite with both
  4. Benchmark performance
  5. Remove native implementation once confident

Milestones

Milestone Deliverable Validation
M1 KiCad builds with deps disabled Opens PCB files, basic editing works
M2 Core library extracts cleanly Compiles as standalone static lib
M3 Core compiles to Wasm Passes unit tests via Node.js
M4 Native app loads Wasm worker Can load/save PCB via Wasm bridge
M5 DRC runs in Wasm Results match native DRC

Design Decisions

Memory Management

Decision: Serialize/deserialize on every operation

  • Proof of concept - simplicity over performance
  • Clean interface between GUI and Wasm core
  • Easy to debug
  • Browser-friendly (no shared memory complexity)
  • Can optimize later if needed
GUI                           Wasm Core
 │                                │
 │──serialize(board)─────────────▶│
 │                                │──process
 │◀─────────────serialize(board)──│
 │                                │

Threading Model

Decision: Web Workers

  • Target is browser, design for it from the start
  • Each heavy operation (DRC, routing) runs in dedicated worker
  • Clean message-passing interface
  • No shared memory complexity
// Main thread spawns workers for heavy operations
const drcWorker = new Worker('drc-worker.js');
drcWorker.postMessage({ board: serializedBoard });
drcWorker.onmessage = (e) => updateUI(e.data.violations);

Incremental Updates

Decision: Re-serialize entire board

  • Simple for proof of concept
  • Board files typically <10MB, acceptable latency
  • Can add delta operations later if performance requires it

Next Steps

  1. Fork KiCad repo, set up as submodule
  2. Write the minimal patch for optional deps
  3. Create stub headers
  4. Verify native build with features disabled
  5. Begin core library extraction