forked from HEL/circuiteria
Defining a block and placing elements into that block has the benefit
that you can use the ports to align the elements within the block to the
ports of that outer ("shell") block
425 lines
13 KiB
Typst
425 lines
13 KiB
Typst
#import "example.typ": example
|
||
|
||
#let alu = example(```
|
||
element.alu(x: 0, y: 0, w: 1, h: 2, id: "alu")
|
||
wire.stub("alu-port-in1", "west")
|
||
wire.stub("alu-port-in2", "west")
|
||
wire.stub("alu-port-out", "east")
|
||
```)
|
||
|
||
#let block = example(```
|
||
element.block(
|
||
x: 0, y: 0, w: 2, h: 2, id: "block",
|
||
ports: (
|
||
north: ((id: "clk", clock: true),),
|
||
west: ((id: "in1", name: "A"),
|
||
(id: "in2", name: "B")),
|
||
east: ((id: "out", name: "C"),)
|
||
)
|
||
)
|
||
wire.stub("block-port-clk", "north")
|
||
wire.stub("block-port-in1", "west")
|
||
wire.stub("block-port-in2", "west")
|
||
wire.stub("block-port-out", "east")
|
||
```)
|
||
|
||
#let extender = example(```
|
||
element.extender(
|
||
x: 0, y: 0, w: 3, h: 1,
|
||
id: "extender"
|
||
)
|
||
wire.stub("extender-port-in", "west")
|
||
wire.stub("extender-port-out", "east")
|
||
```)
|
||
|
||
#let multiplexer = example(```
|
||
element.multiplexer(
|
||
x: 0, y: 0, w: 1, h: 3,
|
||
id: "multiplexer",
|
||
entries: 3
|
||
)
|
||
wire.stub("multiplexer.north", "north")
|
||
wire.stub("multiplexer-port-out", "east")
|
||
|
||
element.multiplexer(
|
||
x: 0, y: -4, w: 1, h: 3,
|
||
id: "multiplexer2",
|
||
entries: ("A", "B", "C")
|
||
)
|
||
wire.stub("multiplexer2.south", "south")
|
||
wire.stub("multiplexer2-port-out", "east")
|
||
|
||
for i in range(3) {
|
||
wire.stub("multiplexer-port-in" + str(i), "west")
|
||
wire.stub("multiplexer2-port-in" + str(i), "west")
|
||
}
|
||
```)
|
||
|
||
#let wires = example(```
|
||
for i in range(5) {
|
||
draw.circle((i * 3, 0), radius: .1, name: "p" + str(i * 2))
|
||
draw.circle((i * 3 + 2, 1), radius: .1, name: "p" + str(i * 2 + 1))
|
||
draw.content((i * 3 + 1, -1), raw(wire.wire-styles.at(i)))
|
||
}
|
||
wire.wire("w1", ("p0", "p1"), style: "direct")
|
||
wire.wire("w2", ("p2", "p3"), style: "zigzag")
|
||
wire.wire("w3", ("p4", "p5"), style: "dodge",
|
||
dodge-y: -0.5, dodge-margins: (0.5, 0.5))
|
||
wire.wire("w4", ("p6","p7"), style: "guided",
|
||
guided-center:(20%, 40%), guided-margins: (90%,87%),
|
||
guided-sides: ("north","south"))
|
||
wire.wire("w5", ("p8", "p9"), style: "u-shape",
|
||
u-side: "east", u-depth: 0.5)
|
||
|
||
```, vertical: true)
|
||
|
||
#let stub = example(```
|
||
draw.circle((0, 0), radius: .1, name: "p")
|
||
wire.stub("p", "north", name: "north", length: 1)
|
||
wire.stub("p", "east", name: "east", vertical: true)
|
||
wire.stub("p", "south", name: "south", length: 15pt)
|
||
wire.stub("p", "west", name: "west", length: 3em)
|
||
```)
|
||
|
||
#let breakout = example(```
|
||
// 16 breakouts: 4 directions × 4 label positions, laid out
|
||
// two per row so the labels have room to breathe.
|
||
let dirs = ("north-east", "north-west", "south-east", "south-west")
|
||
let poss = ("north-east", "north-west", "south-east", "south-west")
|
||
let short = (
|
||
"north-east": "NE", "north-west": "NW",
|
||
"south-east": "SE", "south-west": "SW",
|
||
)
|
||
let cols = 2
|
||
let dx = 5
|
||
let dy = 2
|
||
for (i, dir) in dirs.enumerate() {
|
||
for (j, pos) in poss.enumerate() {
|
||
let n = i * poss.len() + j
|
||
let r = calc.floor(n / cols)
|
||
let c = calc.rem(n, cols)
|
||
draw.circle((c * dx, -r * dy), radius: .05)
|
||
wire.breakout("b" + str(n),
|
||
x: c * dx, y: -r * dy,
|
||
direction: dir,
|
||
port-side: if dir.ends-with("east") { "west" } else { "east" },
|
||
name: short.at(dir) + "/" + short.at(pos),
|
||
name-pos: pos)
|
||
}
|
||
}
|
||
```)
|
||
|
||
#let breakout-label = example(```
|
||
// name-pos matches direction → label sits at the end point.
|
||
wire.breakout("b1", x: 0, y: 0, direction: "north-east",
|
||
port-side: "east",
|
||
name: "at end", name-pos: "north-east")
|
||
|
||
// name-pos opposite to direction → label would cover the line, so
|
||
// it falls back to the start point.
|
||
wire.breakout("b2", x: 4, y: 0, direction: "north-east",
|
||
port-side: "east",
|
||
name: "at start", name-pos: "south-west")
|
||
```, vertical: true)
|
||
|
||
#let breakout-wire = example(```
|
||
// Bus fan-out: a horizontal wire from the left block to the lower
|
||
// right block carries the full 10-bit bus. A breakout in the middle
|
||
// of that wire taps bits [9:5] and routes them to the upper right
|
||
// block via an L-shaped zigzag (up, then right).
|
||
element.block(x: 0, y: 0, w: 2, h: 2, id: "src",
|
||
fill: util.colors.blue,
|
||
ports: (east: ((id: "out"),)))
|
||
element.block(x: 6, y: 0, w: 2, h: 2, id: "dstL",
|
||
fill: util.colors.green,
|
||
ports: (west: ((id: "in"),)))
|
||
element.block(x: 6, y: 4, w: 2, h: 2, id: "dstU",
|
||
fill: util.colors.orange,
|
||
ports: (west: ((id: "in"),)))
|
||
|
||
// Full bus wire, labelled "[9:0]" on the left.
|
||
wire.wire("wbus", ("src-port-out", "dstL-port-in"),
|
||
name: "[9:0]", name-pos: "start")
|
||
|
||
// Breakout tapped from the midpoint of the bus.
|
||
wire.breakout("tap",
|
||
x: (rel: 2, to: "src-port-out"),
|
||
y: (from: "src-port-out"),
|
||
direction: "north-east",
|
||
port-side: "west",
|
||
name: "[9:5]", name-pos: "north-east")
|
||
|
||
// L-shaped wire from the breakout up and then right to the upper
|
||
// block's input.
|
||
wire.wire("wtap", ("tap-port-tap", "dstU-port-in"),
|
||
style: "zigzag", zigzag-dir: "horizontal",
|
||
zigzag-ratio: 100%)
|
||
```)
|
||
|
||
#let gate-and = example(```
|
||
gates.gate-and(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.gate-and(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let gate-nand = example(```
|
||
gates.gate-nand(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.gate-nand(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let gate-buf = example(```
|
||
gates.gate-buf(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.gate-buf(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let gate-not = example(```
|
||
gates.gate-not(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.gate-not(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let gate-or = example(```
|
||
gates.gate-or(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.gate-or(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let gate-nor = example(```
|
||
gates.gate-nor(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.gate-nor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let gate-xor = example(```
|
||
gates.gate-xor(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.gate-xor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let gate-xnor = example(```
|
||
gates.gate-xnor(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.gate-xnor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let iec-gate-and = example(```
|
||
gates.iec-gate-and(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.iec-gate-and(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let iec-gate-nand = example(```
|
||
gates.iec-gate-nand(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.iec-gate-nand(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let iec-gate-or = example(```
|
||
gates.iec-gate-or(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.iec-gate-or(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let iec-gate-nor = example(```
|
||
gates.iec-gate-nor(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.iec-gate-nor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let iec-gate-xor = example(```
|
||
gates.iec-gate-xor(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.iec-gate-xor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let iec-gate-xnor = example(```
|
||
gates.iec-gate-xnor(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.iec-gate-xnor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let iec-gate-buf = example(```
|
||
gates.iec-gate-buf(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.iec-gate-buf(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let iec-gate-not = example(```
|
||
gates.iec-gate-not(x: 0, y: 0, w: 1.5, h: 1.5)
|
||
gates.iec-gate-not(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
|
||
```, vertical: true)
|
||
|
||
#let group = example(```
|
||
element.group(
|
||
id: "g1", name: "Group 1", stroke: (dash: "dashed"),
|
||
{
|
||
element.block(id: "b1", w: 2, h: 2,
|
||
x: 0, y: 1.5,
|
||
ports: (east: ((id: "out"),)),
|
||
fill: util.colors.green
|
||
)
|
||
element.block(id: "b2", w: 2, h: 1,
|
||
x: 0, y: 0,
|
||
ports: (east: ((id: "out"),)),
|
||
fill: util.colors.orange
|
||
)
|
||
}
|
||
)
|
||
element.block(id: "b3", w: 2, h: 3,
|
||
x: (rel: 1, to: "g1.east"),
|
||
y: (from: "b1-port-out", to: "in1"),
|
||
ports: (west: ((id: "in1"), (id: "in2"))),
|
||
fill: util.colors.blue
|
||
)
|
||
wire.wire("w1", ("b1-port-out", "b3-port-in1"))
|
||
wire.wire("w2", ("b2-port-out", "b3-port-in2"),
|
||
style: "zigzag")
|
||
```)
|
||
|
||
#let group-ports = example(```
|
||
// outside-port-names: true (default) — labels outside the border
|
||
element.group(
|
||
id: "g1", name: "outside (default)",
|
||
stroke: (dash: "dashed"),
|
||
fill: util.colors.blue.lighten(60%),
|
||
padding: 2em,
|
||
outside-port-names: true,
|
||
port-name-padding: 4pt,
|
||
ports: (
|
||
west: ((id: "in", name: "D"),),
|
||
east: ((id: "out", name: "Q"),),
|
||
),
|
||
{ element.block(id: "b1", w: 2, h: 1.5, x: 0, y: 0,
|
||
fill: util.colors.green,
|
||
ports: (west: ((id: "d", name: "d"),),
|
||
east: ((id: "q", name: "q"),))) }
|
||
)
|
||
wire.wire("wG1in", ("g1-port-in", "b1-port-d"), directed: true)
|
||
wire.wire("wG1out", ("b1-port-q", "g1-port-out"), directed: true)
|
||
|
||
// outside-port-names: false — labels inside the border (block style)
|
||
// with extra padding via port-name-padding
|
||
element.group(
|
||
id: "g2", name: "inside",
|
||
stroke: (dash: "dashed"),
|
||
fill: util.colors.orange.lighten(60%),
|
||
padding: 2em,
|
||
outside-port-names: false,
|
||
port-name-padding: 0.4em,
|
||
ports: (
|
||
west: ((id: "in", name: "D"),),
|
||
east: ((id: "out", name: "Q"),),
|
||
),
|
||
{ element.block(id: "b2", w: 2, h: 1.5, x: 6, y: 0,
|
||
fill: util.colors.green,
|
||
ports: (west: ((id: "d"),), east: ((id: "q"),))) }
|
||
)
|
||
wire.stub("g2-port-in", "west")
|
||
wire.stub("g2-port-out", "east")
|
||
```, vertical: true)
|
||
|
||
#let port-names-outside = example(```
|
||
element.block(
|
||
x: 0, y: 0, w: 2, h: 2, id: "block",
|
||
port-names-outside: true,
|
||
port-name-padding: 4pt,
|
||
ports: (
|
||
north: ((id: "clk", name: "CLK", clock: true),),
|
||
west: ((id: "in1", name: "A"),
|
||
(id: "in2", name: "B")),
|
||
east: ((id: "out", name: "Y"),)
|
||
)
|
||
)
|
||
```)
|
||
|
||
#let port-shape = example(```
|
||
// Block with pentagon-shaped ports.
|
||
// - port-shape / port-kind / port-size are element-level defaults.
|
||
// - Any port field (shape, kind, size, stroke, fill) overrides per port.
|
||
// - kind: "in" → tip points into the block (default)
|
||
// kind: "out" → tip points away from the block
|
||
// - clock ports with shape: "pentagon" drop their triangle and default
|
||
// to a gray fill.
|
||
element.block(
|
||
id: "b", w: 3, h: 3, x: 0, y: 0,
|
||
fill: util.colors.yellow.lighten(60%),
|
||
port-shape: "pentagon",
|
||
port-size: 1em,
|
||
ports: (
|
||
// west: default input
|
||
west: ((id: "in", name: "D"),),
|
||
// east: output — tip points away
|
||
east: ((id: "out", name: "Q", kind: "out"),),
|
||
// north: clock port — pentagon replaces the triangle, gray fill
|
||
north: ((id: "clk", name: "CLK", clock: true),),
|
||
// south: styled output with per-port fill and stroke
|
||
south: ((id: "rdy", name: "RDY", kind: "out",
|
||
fill: util.colors.blue, stroke: red + 1.5pt),),
|
||
),
|
||
)
|
||
// Wires connect at the outer end of each pentagon.
|
||
wire.stub("b-port-in", "west")
|
||
wire.stub("b-port-out", "east")
|
||
wire.stub("b-port-clk", "north")
|
||
wire.stub("b-port-rdy", "south")
|
||
```)
|
||
|
||
#let shell = example(```
|
||
// A shell block acts as a container: port name labels are drawn
|
||
// outside by default, and wires attach at the *inside* tip of
|
||
// pentagon-shaped ports so child elements can sit within.
|
||
element.block(
|
||
id: "sh", w: 5, h: 3, x: 0, y: 0,
|
||
shell: true,
|
||
port-shape: "pentagon",
|
||
fill: util.colors.blue.lighten(80%),
|
||
ports: (
|
||
west: ((id: "in", name: "IN"),),
|
||
east: ((id: "out", name: "OUT", kind: "out"),),
|
||
),
|
||
{
|
||
element.block(id: "inner", w: 2, h: 1.5, x: 1.5, y: 0.75,
|
||
fill: util.colors.yellow,
|
||
ports: (west: ((id: "a"),), east: ((id: "b"),)))
|
||
}
|
||
)
|
||
wire.wire("w-in", ("sh-port-in", "inner-port-a"))
|
||
wire.wire("w-out", ("inner-port-b", "sh-port-out"))
|
||
```)
|
||
|
||
#let intersection = example(```
|
||
wire.wire("w1", ((0, 0), (1, 1)), style: "zigzag")
|
||
wire.wire("w2", ((0, 0), (1, -.5)),
|
||
style: "zigzag", zigzag-ratio: 80%)
|
||
wire.intersection("w1.zig")
|
||
```)
|
||
|
||
#let u-shape-wire = example(```
|
||
// east <-> east (u-side auto-detected)
|
||
element.block(x: 0, y: 0, w: 1.5, h: 1.5, id: "eA",
|
||
fill: util.colors.orange,
|
||
ports: (east: ((id: "out"),)))
|
||
element.block(x: 0.5, y: -2.5, w: 1.5, h: 1.5, id: "eB",
|
||
fill: util.colors.orange,
|
||
ports: (east: ((id: "out"),)))
|
||
wire.wire("uEE", ("eA-port-out", "eB-port-out"),
|
||
style: "u-shape", u-depth: 0.5)
|
||
|
||
// west <-> west (auto-detected)
|
||
element.block(x: 6, y: 0, w: 1.5, h: 1.5, id: "wA",
|
||
fill: util.colors.yellow,
|
||
ports: (west: ((id: "in"),)))
|
||
element.block(x: 6.5, y: -2.5, w: 1.5, h: 1.5, id: "wB",
|
||
fill: util.colors.yellow,
|
||
ports: (west: ((id: "in"),)))
|
||
wire.wire("uWW", ("wA-port-in", "wB-port-in"),
|
||
style: "u-shape", u-depth: 0.5)
|
||
|
||
// north <-> north (auto-detected)
|
||
element.block(x: 10, y: -3, w: 1.5, h: 1.5, id: "nA",
|
||
fill: util.colors.green,
|
||
ports: (north: ((id: "out"),)))
|
||
element.block(x: 12.5, y: -2.5, w: 1.5, h: 1.5, id: "nB",
|
||
fill: util.colors.green,
|
||
ports: (north: ((id: "out"),)))
|
||
wire.wire("uNN", ("nA-port-out", "nB-port-out"),
|
||
style: "u-shape", u-depth: 0.5)
|
||
|
||
// south <-> south with explicit u-side override
|
||
element.block(x: 16, y: -3, w: 1.5, h: 1.5, id: "sA",
|
||
fill: util.colors.pink,
|
||
ports: (south: ((id: "out"),)))
|
||
element.block(x: 18.5, y: -3.5, w: 1.5, h: 1.5, id: "sB",
|
||
fill: util.colors.pink,
|
||
ports: (south: ((id: "out"),)))
|
||
wire.wire("uSS", ("sA-port-out", "sB-port-out"),
|
||
style: "u-shape", u-side: "south", u-depth: 0.5)
|
||
```, vertical: true)
|