reworked ports layout + adapted multiplexer

This commit is contained in:
Louis Heredero 2025-04-19 15:10:09 +02:00
parent 26231b2f48
commit 4930b53c84
Signed by: HEL
GPG Key ID: 8D83DE470F8544E7
8 changed files with 185 additions and 130 deletions

Binary file not shown.

View File

@ -17,7 +17,7 @@
wire.stub("PCBuf.CLK", name: "CLK")
wire.stub("PCBuf.EN", name: "PCWrite")
/*
element.multiplexer(
pos: (
3, (align: "in0", with: "PCBuf.PC")
@ -25,9 +25,10 @@
size: (1, 2),
id: "AdrSrc-MP",
fill: util.colors.orange,
entries: 2
entries: 2,
debug: (ports: true)
)
wire.wire(
/*wire.wire(
"PCBuf.PC",
"AdrSrc-MP.in0",
id: "wPCBuf-InstDataMgr",

View File

@ -19,6 +19,7 @@
#let block(
..args
) = element.elmt(
cls: "block",
draw-shape: draw-shape,
..args
)

View File

@ -1,5 +1,5 @@
#import "@preview/cetz:0.3.2": draw, coordinate, matrix, vector
#import "ports.typ": add-ports, add-port
#import "ports.typ": add-ports, add-port, get-port-pos, get-port-idx
#import "../util.typ"
#let find-port(ports, id) = {
@ -10,7 +10,7 @@
}
}
}
panic("Could not find port with id " + str(id))
panic("Could not find port with id '" + str(id) + "'")
}
#let local-to-global(origin, u, v, points) = {
@ -42,7 +42,7 @@
return pos.at(axis)
}
#let resolve-align(ctx, elmt, align, with, axis) = {
#let resolve-align(ctx, elmt, bounds, align, with, axis) = {
let (align-side, i) = find-port(elmt.ports, align)
let margins = (0%, 0%)
if align-side in elmt.ports-margins {
@ -67,12 +67,12 @@
let used-len = len * used-pct / 100%
start-margin = len * margins.at(0) / 100%
dl = used-len * (i + 1) / (elmt.ports.at(align-side).len() + 1)
if not elmt.auto-ports {
//dl = used-len * (i + 1) / (elmt.ports.at(align-side).len() + 1)
dl = get-port-pos(elmt, bounds, align-side, align, get-port-idx(elmt, align, side: align-side))
/*if not elmt.auto-ports {
start-margin = 0
dl = elmt.ports-pos.at(with)(len)
}
dl = elmt.ports-pos.at(align)(len)
}*/
} else if align-side == ortho-sides.first() {
dl = 0
start-margin = 0
@ -89,7 +89,7 @@
return with-pos.at(axis) - dl + start-margin
}
#let resolve-coordinate(ctx, elmt, coord, axis) = {
#let resolve-coordinate(ctx, elmt, bounds, coord, axis) = {
if type(coord) == dictionary {
let offset = coord.at("offset", default: none)
let from = coord.at("from", default: none)
@ -105,7 +105,7 @@
return resolve-offset(ctx, offset, from, axis)
} else if none not in (align, with) {
return resolve-align(ctx, elmt, align, with, axis)
return resolve-align(ctx, elmt, bounds, align, with, axis)
} else {
panic("Dictionnary must either provide both 'offset' and 'from', or 'align' and 'with'")
}
@ -116,11 +116,11 @@
return coord
}
#let make-bounds(x, y, w, h) = {
#let make-bounds(elmt, x, y, w, h) = {
let w2 = w / 2
let h2 = h / 2
return (
let bounds = (
bl: (x, y),
tl: (x, y + h),
tr: (x + w, y + h),
@ -131,6 +131,33 @@
l: (x, y + h2),
r: (x + w, y + h2),
)
bounds += (
sides: (
north: (bounds.tl, bounds.tr),
south: (bounds.bl, bounds.br),
west: (bounds.tl, bounds.bl),
east: (bounds.tr, bounds.br),
),
lengths: (
north: (bounds.tr.at(0) - bounds.tl.at(0)),
south: (bounds.br.at(0) - bounds.bl.at(0)),
west: (bounds.tl.at(1) - bounds.bl.at(1)),
east: (bounds.tr.at(1) - bounds.br.at(1)),
),
ports: (:)
)
for (side, props) in bounds.sides.pairs() {
let props2 = props
if side in elmt.ports-margins {
let (pt0, pt1) = props
let margins = ports-margins.at(side)
a = (pt0, margins.at(0), pt1)
b = (pt0, 100% - margins.at(1), pt1)
props2 = (a, b)
}
bounds.ports.insert(side, props2)
}
return bounds
}
#let render(draw-shape, elmt) = draw.group(name: elmt.id, ctx => {
@ -140,10 +167,10 @@
let x = elmt.pos.first()
let y = elmt.pos.last()
x = resolve-coordinate(ctx, elmt, x, 0)
y = resolve-coordinate(ctx, elmt, y, 1)
let bounds = make-bounds(x, y, width, height)
let bounds = make-bounds(elmt, 0, 0, width, height)
x = resolve-coordinate(ctx, elmt, bounds, x, 0)
y = resolve-coordinate(ctx, elmt, bounds, y, 1)
bounds = make-bounds(elmt, x, y, width, height)
// Workaround because CeTZ needs to have all draw functions in the body
let func = {}
@ -171,15 +198,7 @@
)
}
if elmt.auto-ports {
add-ports(
elmt.id,
bounds,
elmt.ports,
elmt.ports-margins,
debug: elmt.debug.ports
)
}
add-ports(elmt, bounds)
})
/// Draws an element
@ -210,6 +229,7 @@
/// Supported fields include:
/// - `ports`: if true, shows dots on all ports of the element
#let elmt(
cls: "element",
draw-shape: default-draw-shape,
pre-process: default-pre-process,
pos: (0, 0),
@ -221,11 +241,11 @@
fill: none,
stroke: black + 1pt,
id: auto,
auto-ports: true,
ports-y: (:),
ports-pos: auto,
debug: (
ports: false
)
),
extra: (:)
) = {
for (key, side-ports) in ports.pairs() {
if type(side-ports) == str {
@ -249,6 +269,7 @@
return ((
cls: cls,
id: id,
draw: render.with(draw-shape),
pre-process: pre-process,
@ -260,8 +281,7 @@
ports-margins: ports-margins,
fill: fill,
stroke: stroke,
auto-ports: auto-ports,
ports-y: ports-y,
ports-pos: ports-pos,
debug: debug
),)
) + extra,)
}

View File

@ -3,29 +3,29 @@
#import "element.typ"
#import "ports.typ": add-port
#let draw-shape(id, tl, tr, br, bl, fill, stroke, h-ratio: 60%) = {
let margin = (100% - h-ratio) / 2
let tr2 = (tr, margin, br)
let br2 = (br, margin, tr)
let f = draw.group(name: id, {
#let draw-shape(elmt, bounds) = {
let margin = (100% - elmt.l-ratio) / 2
let tr2 = (bounds.tr, margin, bounds.br)
let br2 = (bounds.br, margin, bounds.tr)
let f = draw.group(name: elmt.id, {
draw.merge-path(
inset: 0.5em,
fill: fill,
stroke: stroke,
fill: elmt.fill,
stroke: elmt.stroke,
close: true,
draw.line(tl, tr2, br2, bl)
draw.line(bounds.tl, tr2, br2, bounds.bl)
)
draw.anchor("north", (tl, 50%, tr2))
draw.anchor("south", (bl, 50%, br2))
draw.anchor("west", (tl, 50%, bl))
draw.anchor("north", (bounds.tl, 50%, tr2))
draw.anchor("south", (bounds.bl, 50%, br2))
draw.anchor("west", (bounds.tl, 50%, bounds.bl))
draw.anchor("east", (tr2, 50%, br2))
draw.anchor("north-west", tl)
draw.anchor("north-west", bounds.tl)
draw.anchor("north-east", tr2)
draw.anchor("south-east", br2)
draw.anchor("south-west", bl)
draw.anchor("south-west", bounds.bl)
})
return (f, tl, tr, br, bl)
return (f, bounds)
}
/// Draws a multiplexer
@ -35,64 +35,44 @@
/// - entries (int, array): If it is an integer, it defines the number of input ports (automatically named with their binary index). If it is an array of strings, it defines the name of each input.
/// - h-ratio (ratio): The height ratio of the right side relative to the full height
#let multiplexer(
x: none,
y: none,
w: none,
h: none,
name: none,
name-anchor: "center",
entries: 2,
h-ratio: 60%,
fill: none,
stroke: black + 1pt,
id: "",
debug: (
ports: false
)
l-ratio: 60%,
..args
) = {
let ports = ()
let ports-y = (
out: (h) => {h * 0.5}
let in-ports = ()
let ports-pos = (
"east": auto,
)
if (type(entries) == int) {
let nbits = calc.ceil(calc.log(entries, base: 2))
for i in range(entries) {
let bits = util.lpad(str(i, base: 2), nbits)
ports.push((id: "in" + str(i), name: bits))
in-ports.push((id: "in" + str(i), name: bits))
}
} else {
for (i, port) in entries.enumerate() {
ports.push((id: "in" + str(i), name: port))
in-ports.push((id: "in" + str(i), name: port))
}
}
let space = 100% / ports.len()
let l = ports.len()
for (i, port) in ports.enumerate() {
ports-y.insert(port.id, (h) => {h * (i + 0.5) / l})
}
let n = in-ports.len()
ports-pos.insert("west", (l, i) => {l * (i + 0.5) / n})
element.elmt(
draw-shape: draw-shape.with(h-ratio: h-ratio),
x: x,
y: y,
w: w,
h: h,
name: name,
name-anchor: name-anchor,
ports: (west: ports, east: ((id: "out"),)),
fill: fill,
stroke: stroke,
id: id,
ports-y: ports-y,
auto-ports: false,
debug: debug
cls: "multiplexer",
draw-shape: draw-shape,
ports: (west: in-ports, east: ((id: "out"),)),
ports-pos: ports-pos,
extra: (l-ratio: l-ratio),
..args
)
/*
for (i, port) in ports.enumerate() {
let pct = (i + 0.5) * space
add-port(id, "west", port, (id+".north-west", pct, id+".south-west"))
}
add-port(id, "east", (id: "out"), (id+".north-east", 50%, id+".south-east"))
*/
}

View File

@ -1,6 +1,74 @@
#import "@preview/cetz:0.3.2": draw
#import "../util.typ": rotate-anchor
#let get-port-side(elmt, port) = {
for (side, ports) in elmt.ports {
for p in ports {
if p.id == port {
return side
}
}
}
panic("Unknown port " + port + " on element " + element.id)
}
#let get-port-idx(elmt, port, side: auto) = {
if side == auto {
side = get-port-side(elmt, port)
}
assert(
side in elmt.ports,
message: "No ports on side '" + side + "' of element '" + elmt.id + "'"
)
let i = elmt.ports.at(side).position(p => p.id == port)
assert(
i != none,
message: "Could not find port '" + port + "' on side '" + side + "' of element '" + elmt.id + "'"
)
return i
}
#let get-port-pos(elmt, bounds, side, port, port-i) = {
let (pt0, pt1) = bounds.ports.at(side)
let side-len = if side in ("north", "south") {
pt1.at(0) - pt0.at(0)
} else {
pt0.at(1) - pt1.at(1)
}
let offset = if (
elmt.ports-pos == auto or
elmt.ports-pos.at(side, default: auto) == auto
) {
let space = 100% / (elmt.ports.at(side, default: ()).len() + 1)
(port-i + 1) * space
} else {
assert(
side in elmt.ports-pos,
message: "Could not reliably compute port position (missing side)"
)
let side-pos = elmt.ports-pos.at(side)
if type(side-pos) == function {
(side-pos)(side-len, port-i)
} else if type(side-pos) == array {
(side-pos.at(i))(side-len)
} else if type(side-pos) == dictionary {
assert(
port in side-pos,
message: "Could not reliably compute port position (missing port)"
)
(side-pos.at(port))(side-len)
} else {
panic("Could not reliably compute port position (invalid type)")
}
}
if type(offset) == ratio {
offset = offset * side-len / 100%
}
return offset
}
#let add-port(
elmt-id, side, port, pos,
prev: none,
@ -54,46 +122,41 @@
}
#let add-ports(
elmt-id,
bounds,
ports,
ports-margins,
debug: false
elmt,
bounds
) = {
let sides = (
"north": (bounds.tl, bounds.tr),
"east": (bounds.tr, bounds.br),
"south": (bounds.bl, bounds.br),
"west": (bounds.tl, bounds.bl)
)
let debug = elmt.debug.ports
if type(ports) != dictionary {
if type(elmt.ports) != dictionary {
return
}
for (side, props) in sides {
let side-ports = ports.at(side, default: ())
for (side, props) in bounds.ports {
let side-ports = elmt.ports.at(side, default: ())
let space = 100% / (side-ports.len() + 1)
let (pt0, pt1) = props
let side-len = if side in ("north", "south") {
pt1.at(0) - pt0.at(0)
} else {
pt0.at(1) - pt1.at(1)
}
for (i, port) in side-ports.enumerate() {
let pct = (i + 1) * space
let pt0 = props.at(0)
let pt1 = props.at(1)
let offset = get-port-pos(elmt, bounds, side, port, i)
if side in ports-margins {
let (a, b) = (pt0, pt1)
let margins = ports-margins.at(side)
a = (pt0, margins.at(0), pt1)
b = (pt0, 100% - margins.at(1), pt1)
pt0 = a
pt1 = b
let pos = (pt0, offset, pt1)
let offset-prev = if type(offset) == ratio {
offset - space / 2
} else {
offset - space * side-len / 200%
}
let pos = (pt0, pct, pt1)
let pct-prev = (i + 0.5) * space
let pct-next = (i + 1.5) * space
let pos-prev = (pt0, pct-prev, pt1)
let pos-next = (pt0, pct-next, pt1)
let offset-next = if type(offset) == ratio {
offset + space / 2
} else {
offset + space * side-len / 200%
}
let pos-prev = (pt0, offset-prev, pt1)
let pos-next = (pt0, offset-next, pt1)
if port.at("small", default: false) {
pos-prev = (pos, 4pt, pt0)
@ -101,7 +164,7 @@
}
add-port(
elmt-id,
elmt.id,
side,
port,
pos,

View File

@ -73,15 +73,4 @@
#let valid-anchors = (
"center", "north", "east", "west", "south",
"north-east", "north-west", "south-east", "south-west"
)
#let get-port-side(element, port) = {
for (side, ports) in element.ports {
for p in ports {
if p.id == port {
return side
}
}
}
panic("Unknown port " + port + " on element " + element.id)
}
)

View File

@ -1,5 +1,6 @@
#import "@preview/cetz:0.3.2": draw, coordinate
#import "util.typ": opposite-anchor, get-port-side
#import "util.typ": opposite-anchor
#import "elements/ports.typ": get-port-side
#import "elements/element.typ"
/// List of valid wire styles