Change npm examples to scmos examples
This commit is contained in:
parent
354416f717
commit
c8b50a702d
|
|
@ -22,11 +22,12 @@ import createMagic from 'magic-vlsi-wasm';
|
||||||
|
|
||||||
const { runCommand, FS } = await createMagic();
|
const { runCommand, FS } = await createMagic();
|
||||||
|
|
||||||
// Write a layout into Magic's virtual filesystem
|
// Drop a layout into Magic's virtual filesystem
|
||||||
|
FS.mkdirTree('/work');
|
||||||
FS.writeFile('/work/inv.mag', layoutBytes);
|
FS.writeFile('/work/inv.mag', layoutBytes);
|
||||||
|
|
||||||
// Run Magic commands
|
// Run Magic commands — scmos is built into the WASM binary, no tech file needed
|
||||||
runCommand('tech load minimum');
|
runCommand('tech load scmos');
|
||||||
runCommand('load /work/inv');
|
runCommand('load /work/inv');
|
||||||
runCommand('gds write /work/inv');
|
runCommand('gds write /work/inv');
|
||||||
|
|
||||||
|
|
@ -35,8 +36,9 @@ const gdsBytes = FS.readFile('/work/inv.gds');
|
||||||
```
|
```
|
||||||
|
|
||||||
The `scmos` technology family (`scmos`, `minimum`, `nmos`, ...) is embedded in
|
The `scmos` technology family (`scmos`, `minimum`, `nmos`, ...) is embedded in
|
||||||
the WASM binary and available out of the box. Custom tech files can be written
|
the WASM binary and available out of the box — those names work without
|
||||||
into the VFS at `/magic/sys/current/<name>.tech`.
|
writing any tech file. To use a custom technology, write its `.tech` file into
|
||||||
|
the VFS at `/magic/sys/current/<name>.tech` before calling `tech load <name>`.
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
|
|
@ -57,13 +59,34 @@ The returned `MagicInstance` exposes:
|
||||||
| Method | Description |
|
| Method | Description |
|
||||||
|---------------------------|-------------|
|
|---------------------------|-------------|
|
||||||
| `runCommand(cmd: string)` | Dispatch a single Magic command. Returns 0 on success. |
|
| `runCommand(cmd: string)` | Dispatch a single Magic command. Returns 0 on success. |
|
||||||
| `sourceFile(path: string)` | Execute a script from the virtual filesystem. |
|
| `sourceFile(path: string)` | Execute a script from the virtual filesystem. Returns 0 on success, -1 if the file could not be opened. |
|
||||||
| `init()` | Force initialization. Idempotent — `runCommand` and `sourceFile` call it for you. |
|
| `init()` | Force initialization. Idempotent — `runCommand` and `sourceFile` call it for you. |
|
||||||
| `update()` | Drive a display-update cycle. No-op in this headless build. |
|
| `update()` | Drive a display-update cycle. No-op in this headless build. |
|
||||||
| `FS` | Emscripten virtual filesystem. See the [Emscripten docs](https://emscripten.org/docs/api_reference/Filesystem-API.html). |
|
| `FS` | Emscripten virtual filesystem. See the [Emscripten docs](https://emscripten.org/docs/api_reference/Filesystem-API.html). |
|
||||||
|
|
||||||
Full TypeScript types ship in [`index.d.ts`](index.d.ts).
|
Full TypeScript types ship in [`index.d.ts`](index.d.ts).
|
||||||
|
|
||||||
|
### Low-level access
|
||||||
|
|
||||||
|
`createMagic()` is a thin convenience wrapper over the underlying Emscripten
|
||||||
|
module. If you need direct access — for example to call `cwrap` yourself or
|
||||||
|
to drive `magic_wasm_init` manually — import the module factory directly:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import createMagicModule from 'magic-vlsi-wasm/magic.js';
|
||||||
|
|
||||||
|
const module = await createMagicModule({ wasmBinary, print, printErr });
|
||||||
|
module._magic_wasm_init();
|
||||||
|
const run = module.cwrap('magic_wasm_run_command', 'number', ['string']);
|
||||||
|
run('tech load scmos');
|
||||||
|
```
|
||||||
|
|
||||||
|
The bundled examples use this lower-level path together with a small helper
|
||||||
|
class ([`examples/helpers.js`](examples/helpers.js)) that adds a
|
||||||
|
`runScript(text)` convenience method — it splits a multi-line Tcl block,
|
||||||
|
strips comments, and dispatches each line via `runCommand`. Useful when you
|
||||||
|
have a script as a string rather than as a file in the VFS.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
The package ships runnable examples for the most common workflows. After
|
The package ships runnable examples for the most common workflows. After
|
||||||
|
|
@ -80,13 +103,17 @@ Or, when developing inside this repo:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm test # full suite (extract, gds, drc, cif)
|
npm test # full suite (extract, gds, drc, cif)
|
||||||
|
npm run example # extract.js — RC extraction + SPICE netlist
|
||||||
npm run test:gds # GDS write only
|
npm run test:gds # GDS write only
|
||||||
npm run test:drc # DRC check only
|
npm run test:drc # DRC check only
|
||||||
npm run test:cif # CIF write only
|
npm run test:cif # CIF write only
|
||||||
```
|
```
|
||||||
|
|
||||||
Each example is self-contained and reads `examples/siliwiz.{mag,tech}` by
|
Each example loads the bundled [`min.mag`](examples/min.mag) (a small NPN
|
||||||
default. See [`examples/`](examples/) for the source.
|
transistor cell from Magic's own scmos test suite) under the built-in `scmos`
|
||||||
|
technology — no external tech file required. See [`examples/`](examples/) for
|
||||||
|
the source; [`example.js`](examples/example.js) is the simplest entry point
|
||||||
|
(GDS → CIF conversion in ~40 lines).
|
||||||
|
|
||||||
## Build from source
|
## Build from source
|
||||||
|
|
||||||
|
|
@ -117,11 +144,10 @@ checkout (Magic pins emsdk `3.1.56` — see the comment in `npm/build.sh`).
|
||||||
|
|
||||||
[HPND](LICENSE) — Copyright (C) 1985, 1990 Regents of the University of California.
|
[HPND](LICENSE) — Copyright (C) 1985, 1990 Regents of the University of California.
|
||||||
|
|
||||||
### Bundled test technology
|
### Bundled test layout
|
||||||
|
|
||||||
The example layout (`examples/siliwiz.mag`) and technology file
|
The example layout [`examples/min.mag`](examples/min.mag) is taken from
|
||||||
(`examples/siliwiz.tech`) are derived from the
|
Magic's own scmos test suite ([`scmos/examples/bipolar/min.mag`](../scmos/examples/bipolar/min.mag))
|
||||||
[SiliWiz](https://github.com/wokwi/siliwiz) educational silicon design
|
and is included here as a runnable smoke test for the WASM build. The `scmos`
|
||||||
tool. They are bundled here only as a runnable smoke test for the WASM
|
technology it targets is compiled into the WASM binary, so no external tech
|
||||||
build. The technology file is © R. Timothy Edwards, Open Circuit Design,
|
file is shipped.
|
||||||
2023, marked by the author as containing no proprietary information.
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
// cif.js — Export layout to CIF (Caltech Intermediate Form).
|
// cif.js — Export layout to CIF (Caltech Intermediate Form).
|
||||||
//
|
//
|
||||||
// Usage: node examples/cif.js [magFile [techFile [outputDir]]]
|
// Usage: node examples/cif.js [magFile [tech [outputDir]]]
|
||||||
import { createMagic, vfsRead, loadCell, loadScript,
|
import { createMagic, vfsRead, loadCell, loadScript,
|
||||||
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.js';
|
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.js';
|
||||||
import { writeFileSync, mkdirSync } from 'node:fs';
|
import { writeFileSync, mkdirSync } from 'node:fs';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { resolve } from 'node:path';
|
import { resolve } from 'node:path';
|
||||||
|
|
||||||
export async function run({ magFile = DEFAULT_MAG, techFile = DEFAULT_TECH, outputDir = DEFAULT_OUT } = {}) {
|
export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH, outputDir = DEFAULT_OUT } = {}) {
|
||||||
const { magic } = await createMagic();
|
const { magic } = await createMagic();
|
||||||
const { FS } = magic;
|
const { FS } = magic;
|
||||||
const { tech, cell } = loadCell(FS, techFile, magFile);
|
const { tech: techName, cell } = loadCell(FS, tech, magFile);
|
||||||
|
|
||||||
magic.runScript(loadScript('cif.tcl', tech, cell));
|
magic.runScript(loadScript('cif.tcl', techName, cell));
|
||||||
|
|
||||||
mkdirSync(outputDir, { recursive: true });
|
mkdirSync(outputDir, { recursive: true });
|
||||||
const data = vfsRead(FS, `/work/${cell}.cif`);
|
const data = vfsRead(FS, `/work/${cell}.cif`);
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
// drc.js — Run design rule checking and report violations.
|
// drc.js — Run design rule checking and report violations.
|
||||||
//
|
//
|
||||||
// Usage: node examples/drc.js [magFile [techFile]]
|
// Usage: node examples/drc.js [magFile [tech]]
|
||||||
import { createMagic, loadCell, loadScript,
|
import { createMagic, loadCell, loadScript,
|
||||||
DEFAULT_TECH, DEFAULT_MAG } from './helpers.js';
|
DEFAULT_TECH, DEFAULT_MAG } from './helpers.js';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
export async function run({ magFile = DEFAULT_MAG, techFile = DEFAULT_TECH } = {}) {
|
export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH } = {}) {
|
||||||
const output = [];
|
const output = [];
|
||||||
const { magic } = await createMagic({
|
const { magic } = await createMagic({
|
||||||
onPrint: msg => { output.push(msg); console.log('[magic]', msg); },
|
onPrint: msg => { output.push(msg); console.log('[magic]', msg); },
|
||||||
onPrintErr: msg => { output.push(msg); console.error('[magic]', msg); },
|
onPrintErr: msg => { output.push(msg); console.error('[magic]', msg); },
|
||||||
});
|
});
|
||||||
const { FS } = magic;
|
const { FS } = magic;
|
||||||
const { tech, cell } = loadCell(FS, techFile, magFile);
|
const { tech: techName, cell } = loadCell(FS, tech, magFile);
|
||||||
|
|
||||||
magic.runScript(loadScript('drc.tcl', tech, cell));
|
magic.runScript(loadScript('drc.tcl', techName, cell));
|
||||||
|
|
||||||
// Magic prints "Total DRC errors found: N" at the end of drc listall.
|
// Magic prints "Total DRC errors found: N" at the end of drc listall.
|
||||||
const summary = output.find(l => /Total DRC errors/i.test(l));
|
const summary = output.find(l => /Total DRC errors/i.test(l));
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
// extract.js — RC extraction example (extract → extresist → ext2spice).
|
// extract.js — RC extraction example (extract → extresist → ext2spice).
|
||||||
//
|
//
|
||||||
// Usage: node examples/extract.js [magFile [techFile [outputDir]]]
|
// Usage: node examples/extract.js [magFile [tech [outputDir]]]
|
||||||
import { createMagic, vfsWrite, vfsRead, loadCell, loadScript,
|
import { createMagic, vfsWrite, vfsRead, loadCell, loadScript,
|
||||||
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.js';
|
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.js';
|
||||||
import { writeFileSync, mkdirSync } from 'node:fs';
|
import { writeFileSync, mkdirSync } from 'node:fs';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { resolve } from 'node:path';
|
import { resolve } from 'node:path';
|
||||||
|
|
||||||
export async function run({ magFile = DEFAULT_MAG, techFile = DEFAULT_TECH, outputDir = DEFAULT_OUT } = {}) {
|
export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH, outputDir = DEFAULT_OUT } = {}) {
|
||||||
const { magic } = await createMagic();
|
const { magic } = await createMagic();
|
||||||
const { FS } = magic;
|
const { FS } = magic;
|
||||||
const { tech, cell } = loadCell(FS, techFile, magFile);
|
const { tech: techName, cell } = loadCell(FS, tech, magFile);
|
||||||
|
|
||||||
magic.runScript(loadScript('extract.tcl', tech, cell));
|
magic.runScript(loadScript('extract.tcl', techName, cell));
|
||||||
|
|
||||||
mkdirSync(outputDir, { recursive: true });
|
mkdirSync(outputDir, { recursive: true });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
// gds.js — Export layout to GDS II stream format.
|
// gds.js — Export layout to GDS II stream format.
|
||||||
//
|
//
|
||||||
// Usage: node examples/gds.js [magFile [techFile [outputDir]]]
|
// Usage: node examples/gds.js [magFile [tech [outputDir]]]
|
||||||
import { createMagic, vfsRead, loadCell, loadScript,
|
import { createMagic, vfsRead, loadCell, loadScript,
|
||||||
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.js';
|
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.js';
|
||||||
import { writeFileSync, mkdirSync } from 'node:fs';
|
import { writeFileSync, mkdirSync } from 'node:fs';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { resolve } from 'node:path';
|
import { resolve } from 'node:path';
|
||||||
|
|
||||||
export async function run({ magFile = DEFAULT_MAG, techFile = DEFAULT_TECH, outputDir = DEFAULT_OUT } = {}) {
|
export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH, outputDir = DEFAULT_OUT } = {}) {
|
||||||
const { magic } = await createMagic();
|
const { magic } = await createMagic();
|
||||||
const { FS } = magic;
|
const { FS } = magic;
|
||||||
const { tech, cell } = loadCell(FS, techFile, magFile);
|
const { tech: techName, cell } = loadCell(FS, tech, magFile);
|
||||||
|
|
||||||
magic.runScript(loadScript('gds.tcl', tech, cell));
|
magic.runScript(loadScript('gds.tcl', techName, cell));
|
||||||
|
|
||||||
mkdirSync(outputDir, { recursive: true });
|
mkdirSync(outputDir, { recursive: true });
|
||||||
const data = vfsRead(FS, `/work/${cell}.gds`);
|
const data = vfsRead(FS, `/work/${cell}.gds`);
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import { dirname, resolve, basename } from 'node:path';
|
||||||
|
|
||||||
export const EXAMPLES_DIR = dirname(fileURLToPath(import.meta.url));
|
export const EXAMPLES_DIR = dirname(fileURLToPath(import.meta.url));
|
||||||
export const wasmBinary = readFileSync(resolve(EXAMPLES_DIR, '../magic.wasm'));
|
export const wasmBinary = readFileSync(resolve(EXAMPLES_DIR, '../magic.wasm'));
|
||||||
export const DEFAULT_TECH = resolve(EXAMPLES_DIR, 'siliwiz.tech');
|
export const DEFAULT_TECH = 'scmos';
|
||||||
export const DEFAULT_MAG = resolve(EXAMPLES_DIR, 'siliwiz.mag');
|
export const DEFAULT_MAG = resolve(EXAMPLES_DIR, 'min.mag');
|
||||||
export const DEFAULT_OUT = resolve(EXAMPLES_DIR, 'output');
|
export const DEFAULT_OUT = resolve(EXAMPLES_DIR, 'output');
|
||||||
|
|
||||||
export { basename };
|
export { basename };
|
||||||
|
|
@ -55,12 +55,12 @@ export async function createMagic({ onPrint, onPrintErr } = {}) {
|
||||||
return { magic, lines };
|
return { magic, lines };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads tech + mag into VFS using standard paths.
|
// Loads a layout into the VFS. `tech` is the name of a technology that is
|
||||||
export function loadCell(FS, techFile, magFile) {
|
// either built into the WASM binary (e.g. 'scmos', 'minimum', 'nmos') or has
|
||||||
const tech = basename(techFile, '.tech');
|
// already been written to /magic/sys/current/<tech>.tech by the caller.
|
||||||
const cell = basename(magFile, '.mag');
|
export function loadCell(FS, tech, magFile) {
|
||||||
vfsWrite(FS, `/magic/sys/current/${tech}.tech`, techFile);
|
const cell = basename(magFile, '.mag');
|
||||||
vfsWrite(FS, `/work/${cell}.mag`, magFile);
|
vfsWrite(FS, `/work/${cell}.mag`, magFile);
|
||||||
return { tech, cell };
|
return { tech, cell };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
magic
|
||||||
|
tech scmos
|
||||||
|
timestamp 783737348
|
||||||
|
<< nwell >>
|
||||||
|
rect 5 3 46 27
|
||||||
|
<< metal1 >>
|
||||||
|
rect 9 0 13 13
|
||||||
|
rect 22 0 26 13
|
||||||
|
rect 33 0 37 13
|
||||||
|
<< collector >>
|
||||||
|
rect 8 17 14 18
|
||||||
|
rect 8 13 9 17
|
||||||
|
rect 13 13 14 17
|
||||||
|
rect 8 12 14 13
|
||||||
|
<< pbase >>
|
||||||
|
rect 18 17 40 21
|
||||||
|
rect 18 13 22 17
|
||||||
|
rect 26 13 33 17
|
||||||
|
rect 37 13 40 17
|
||||||
|
rect 18 9 40 13
|
||||||
|
<< collectorcontact >>
|
||||||
|
rect 9 13 13 17
|
||||||
|
<< emittercontact >>
|
||||||
|
rect 22 13 26 17
|
||||||
|
<< pbasecontact >>
|
||||||
|
rect 33 13 37 17
|
||||||
|
<< labels >>
|
||||||
|
rlabel space 0 17 0 17 3 without
|
||||||
|
rlabel space 0 13 0 13 3 guardring
|
||||||
|
rlabel metal1 35 1 35 1 5 base
|
||||||
|
rlabel metal1 24 1 24 1 5 emitter
|
||||||
|
rlabel metal1 11 1 11 1 5 collector
|
||||||
|
<< end >>
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
magic
|
|
||||||
tech siliwiz
|
|
||||||
magscale 1 1
|
|
||||||
timestamp 1776371010
|
|
||||||
<< nwell >>
|
|
||||||
rect 95 221 349 377
|
|
||||||
<< ndiffusion >>
|
|
||||||
rect 132 85 270 131
|
|
||||||
<< pdiffusion >>
|
|
||||||
rect 132 254 270 316
|
|
||||||
<< psubstratepdiff >>
|
|
||||||
rect 319 0 382 58
|
|
||||||
<< nsubstratendiff >>
|
|
||||||
rect 291 309 338 361
|
|
||||||
<< polysilicon >>
|
|
||||||
rect 178 51 217 327
|
|
||||||
rect 148 179 201 220
|
|
||||||
<< pdcontact >>
|
|
||||||
rect 233 264 266 307
|
|
||||||
rect 132 259 169 312
|
|
||||||
<< ndcontact >>
|
|
||||||
rect 236 89 268 127
|
|
||||||
rect 132 88 166 128
|
|
||||||
<< polycontact >>
|
|
||||||
rect 147 185 172 214
|
|
||||||
<< nsubstratencontact >>
|
|
||||||
rect 295 330 335 364
|
|
||||||
<< psubstratepcontact >>
|
|
||||||
rect 333 8 377 49
|
|
||||||
<< metal1 >>
|
|
||||||
rect 39 179 127 218
|
|
||||||
rect 270 180 366 218
|
|
||||||
rect 110 179 173 216
|
|
||||||
rect 229 89 270 307
|
|
||||||
rect 244 180 284 214
|
|
||||||
rect 128 264 164 366
|
|
||||||
rect 132 13 166 130
|
|
||||||
rect 40 9 374 49
|
|
||||||
rect 42 334 358 373
|
|
||||||
rect 29 328 80 377
|
|
||||||
rect 30 6 81 56
|
|
||||||
<< labels >>
|
|
||||||
flabel metal1 s 39 179 127 218 0 FreeSans 240 90 0 0 in
|
|
||||||
port 2 nsew signal output
|
|
||||||
flabel metal1 s 270 180 366 218 0 FreeSans 240 90 0 0 out
|
|
||||||
port 3 nsew signal output
|
|
||||||
flabel metal1 s 29 328 80 377 0 FreeSans 240 90 0 0 vdd
|
|
||||||
port 4 nsew signal output
|
|
||||||
flabel metal1 s 30 6 81 56 0 FreeSans 240 90 0 0 vss
|
|
||||||
port 5 nsew signal output
|
|
||||||
<< end >>
|
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue