From c9155a0b992d3fef9735a1a969687b1859aaab6b Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Thu, 16 May 2024 23:35:53 +0200 Subject: [PATCH] initial commit --- main.typ | 1039 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1039 insertions(+) create mode 100644 main.typ diff --git a/main.typ b/main.typ new file mode 100644 index 0000000..82993b0 --- /dev/null +++ b/main.typ @@ -0,0 +1,1039 @@ +#import "@preview/cetz:0.2.2": canvas, draw, coordinate, vector + +#set text(font: "Source Sans Pro") +#set page(flipped: true) + +#let diag-colors = ( + orange: rgb(245, 180, 147), + yellow: rgb(250, 225, 127), + green: rgb(127, 200, 172), + pink: rgb(236, 127, 178), + purple: rgb(189, 151, 255) +) + +#let lpad(s, len) = { + let res = "0" * len + s + return res.slice(-len) +} + +#let opposite-anchor(anchor) = { + return ( + north: "south", + east: "west", + south: "north", + west: "east", + + north-west: "south-east", + north-east: "south-west", + south-east: "north-west", + south-west: "north-east" + ).at(anchor) +} + +#let diag-port-stub(port-id, side, name: none, vertical: false, length: 1em) = { + + let offset = ( + north: (0, length), + east: (length, 0), + south: (0, -length), + west: (-length, 0) + ).at(side) + draw.line( + port-id, + (rel: offset, to: port-id) + ) + if name != none { + let text-anchor = if vertical { + ( + "north": "west", + "south": "east", + "west": "south", + "east": "north" + ).at(side) + } else { opposite-anchor(side) } + draw.content( + anchor: text-anchor, + padding: 0.2em, + angle: if vertical {90deg} else {0deg}, + (rel: offset, to: port-id), + name + ) + } +} + +#let diag-port( + elmt-id, + side, + port, + pos, + prev: none, + next: none, + debug: false +) = { + let name = port.at("name", default: "") + let name-rotate = port.at("vertical", default: false) + + if (port.at("clock", default: false)) { + if prev == none or next == none { + panic("Clock port must have previous and next positions") + } + + let offset + if (side == "north") { + offset = (0, -1em) + } else if (side == "east") { + offset = (-1em, 0) + } else if (side == "south") { + offset = (0, 1em) + } else if (side == "west") { + offset = (1em, 0) + } + let pos1 = (rel: offset, to: pos) + + // TODO: use context or vectors to have the height relative to the width + draw.line( + prev, pos1, next + ) + } + draw.content( + pos, + anchor: if name-rotate { + ( + "north": "east", + "south": "west", + "west": "north", + "east": "south" + ).at(side) + } else {side}, + padding: 2pt, + angle: if name-rotate {90deg} else {0deg}, + name + ) + if debug { + draw.circle( + pos, + name: elmt-id + "-port-" + port.at("id"), + radius: .1, + stroke: none, + fill: red + ) + + } else { + draw.hide(draw.circle( + pos, + radius: 0, + stroke: none, + name: elmt-id + "-port-" + port.at("id") + )) + } +} + +#let diag-ports( + elmt-id, + tl, tr, br, bl, + ports, + ports-margins, + debug: false +) = { + let sides = ( + "north": (tl, tr), + "east": (tr, br), + "south": (bl, br), + "west": (tl, bl) + ) + for (side, props) in sides { + let side-ports = ports.at(side, default: ()) + for (i, port) in side-ports.enumerate() { + let pct = (i + 1) * 100% / (side-ports.len() + 1) + let pt0 = props.at(0) + let pt1 = props.at(1) + + 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, pct, pt1) + + let pct-prev = (i + 0.5) * 100% / (side-ports.len() + 1) + let pct-next = (i + 1.5) * 100% / (side-ports.len() + 1) + let pos-prev = (pt0, pct-prev, pt1) + let pos-next = (pt0, pct-next, pt1) + diag-port( + elmt-id, + side, + port, + pos, + prev: pos-prev, + next: pos-next, + debug: debug + ) + /* + + let name = port.at("name", default: "") + let name-rotate = port.at("vertical", default: false) + + if (port.at("clock", default: false)) { + let pct-prev = (i + 0.5) * 100% / (side-ports.len() + 1) + let pct-next = (i + 1.5) * 100% / (side-ports.len() + 1) + let pos0 = (pt0, pct-prev, pt1) + let offset + if (side == "north") { + offset = (0, -1em) + } else if (side == "east") { + offset = (-1em, 0) + } else if (side == "south") { + offset = (0, 1em) + } else if (side == "west") { + offset = (1em, 0) + } + let pos1 = (rel: offset, to: pos) + let pos2 = (pt0, pct-next, pt1) + + // TODO: use context or vectors to have the height relative to the width + draw.line( + pos0, pos1, pos2 + ) + } + draw.content( + pos, + anchor: if name-rotate { + ( + "north": "east", + "south": "west", + "west": "north", + "east": "south" + ).at(side) + } else {side}, + padding: 2pt, + angle: if name-rotate {90deg} else {0deg}, + name + ) + if debug { + draw.circle( + pos, + name: elmt-id + "-port-" + port.at("id"), + radius: .1, + stroke: none, + fill: red + ) + + } else { + draw.hide(draw.circle( + pos, + radius: 0, + stroke: none, + name: elmt-id + "-port-" + port.at("id") + )) + }*/ + } + } +} + +#let diag-find-port(ports, id) = { + for (side, side-ports) in ports { + for (i, port) in side-ports.enumerate() { + if port.id == id { + return (side, i) + } + } + } + panic("Could not find port with id " + str(id)) +} + +#let diag-elmt( + appearance: "rect", + x: none, + y: none, + w: none, + h: none, + name: none, + name-pos: "center", + ports: (), + ports-margins: (), + fill: none, + stroke: black + 1pt, + id: "", + debug: ( + grid: false, + ports: false + ) +) = draw.get-ctx(ctx => { + let width = w + let height = h + + let y = y + if x == none { panic("Parameter x must be set") } + if y == none { panic("Parameter y must be set") } + if w == none { panic("Parameter w must be set") } + if h == none { panic("Parameter h must be set") } + + if (type(y) == dictionary) { + let from = y.from + let to = y.to + let (to-side, i) = diag-find-port(ports, to) + let margins = (0%, 0%) + if to-side in ports-margins { + margins = ports-margins.at(to-side) + } + let used-pct = 100% - margins.at(0) - margins.at(1) + let used-height = height * used-pct / 100% + let top-margin = height * margins.at(0) / 100% + + let dy = used-height * (i + 1) / (ports.at(to-side).len() + 1) + + if appearance == "alu" { + top-margin = 0 + if to == "in1" { + dy = height * 0.225 + } else if to == "in2" { + dy = height * 0.775 + } + } + + let (ctx, from-pos) = coordinate.resolve(ctx, from) + y = from-pos.at(1) + dy - height + top-margin + } + + let tl = (x, y + height) + let tr = (x + width, y + height) + let br = (x + width, y) + let bl = (x, y) + + if appearance == "rect" { + draw.rect( + radius: 0.5em, + inset: 0.5em, + fill: fill, + stroke: stroke, + name: id, + bl, tr + ) + } else if appearance == "multiplexer" { + let tr2 = (tr, 20%, br) + let br2 = (tr, 80%, br) + draw.group(name: id, { + draw.merge-path( + inset: 0.5em, + fill: fill, + stroke: stroke, + close: true, + draw.line(tl, tr2, br2, bl) + ) + draw.anchor("north", (tl, 50%, tr2)) + draw.anchor("south", (bl, 50%, br2)) + draw.anchor("west", (tl, 50%, bl)) + draw.anchor("east", (tr2, 50%, br2)) + }) + + } else if appearance == "extender" { + tl = (x, y + height * 0.75) + let tr2 = (x + width, y + height * 0.75) + let br = (x + width, y) + (tr, tr2) = (tr2, tr) + draw.group(name: id, { + draw.merge-path( + inset: 0.5em, + fill: fill, + stroke: stroke, + close: true, + draw.line(tl, tr2, br, bl) + ) + draw.anchor("north", (tl, 50%, tr2)) + draw.anchor("south", (bl, 50%, br)) + draw.anchor("west", (tl, 50%, bl)) + draw.anchor("east", (tr2, 50%, br)) + }) + } else if appearance == "alu" { + let p0 = tl + let p1 = (tr, 10%, br) + let p2 = (tr, 90%, br) + let p3 = bl + let p4 = (tl, 55%, bl) + let p5 = (tl, 50%, br) + let p6 = (tl, 45%, bl) + + draw.group(name: id, { + + draw.merge-path( + inset: 0.5em, + fill: fill, + stroke: stroke, + close: true, + draw.line(p0, p1, p2, p3, p4, p5, p6) + ) + draw.anchor("north", (p0, 50%, p1)) + draw.anchor("south", (p2, 50%, p3)) + draw.anchor("west", (p0, 50%, p3)) + draw.anchor("east", (p1, 50%, p2)) + }) + + diag-port(id, "west", (id: "in1"), (p0, 50%, p6)) + diag-port(id, "west", (id: "in2"), (p3, 50%, p4)) + diag-port(id, "east", (id: "out"), (p1, 50%, p2)) + } + /*draw.for-each-anchor(id, (name) => { + draw.circle(id + "." + name, radius: .1, fill: red) + })*/ + + if (name != none) { + let block-anchor = "center" + let name-anchor = "center" + if (name-pos == "top") { + block-anchor = "north" + name-anchor = "north" + } else if (name-pos == "bottom") { + block-anchor = "south" + name-anchor = "south" + } else if (name-pos == "left") { + block-anchor = "west" + name-anchor = "west" + } else if (name-pos == "right") { + block-anchor = "east" + name-anchor = "east" + } + draw.content( + (name: id, anchor: block-anchor), + anchor: name-anchor, + padding: 0.5em, + align(center)[*#name*] + ) + + } + if appearance != "alu" { + diag-ports( + id, + tl, tr, br, bl, + ports, + ports-margins, + debug: debug.ports + ) + } +}) + +#let diag-block( + x: none, + y: none, + w: none, + h: none, + name: none, + name-pos: "center", + ports: (), + ports-margins: (), + fill: none, + stroke: black + 1pt, + id: "", + debug: ( + grid: false, + ports: false + ) +) = diag-elmt( + appearance: "rect", + x: x, + y: y, + w: w, + h: h, + name: name, + name-pos: name-pos, + ports: ports, + ports-margins: ports-margins, + fill: fill, + stroke: stroke, + id: id, + debug: debug +) + +#let diag-multiplexer( + x: none, + y: none, + w: none, + h: none, + name: none, + name-pos: "center", + entries: 2, + fill: none, + stroke: black + 1pt, + id: "", + debug: ( + grid: false, + ports: false + ) +) = { + let ports = () + + if (type(entries) == int) { + let nbits = calc.ceil(calc.log(entries, base: 2)) + for i in range(entries) { + let bits = lpad(str(i, base: 2), nbits) + ports.push((id: "in" + str(i), name: bits)) + } + } + + diag-elmt( + appearance: "multiplexer", + x: x, + y: y, + w: w, + h: h, + name: name, + name-pos: name-pos, + ports: (west: ports, east: ((id: "out"),)), + fill: fill, + stroke: stroke, + id: id, + debug: debug + ) +} + +#let diag-extender( + x: none, + y: none, + w: none, + h: none, + name: none, + name-pos: "center", + fill: none, + stroke: black + 1pt, + id: "", + debug: ( + grid: false, + ports: false + ) +) = { + let ports = ( + west: ( + (id: "in"), + ), + east: ( + (id: "out"), + ) + ) + + diag-elmt( + appearance: "extender", + x: x, + y: y, + w: w, + h: h, + name: name, + name-pos: name-pos, + ports: ports, + fill: fill, + stroke: stroke, + id: id, + debug: debug + ) +} + +#let diag-alu( + x: none, + y: none, + w: none, + h: none, + name: none, + name-pos: "center", + fill: none, + stroke: black + 1pt, + id: "", + debug: ( + grid: false, + ports: false + ) +) = { + let ports = ( + west: ( + (id: "in1"), + (id: "in2"), + ), + east: ( + (id: "out"), + ) + ) + + diag-elmt( + appearance: "alu", + x: x, + y: y, + w: w, + h: h, + name: name, + name-pos: name-pos, + ports: ports, + fill: fill, + stroke: stroke, + id: id, + debug: debug + ) +} + +#let diag-wire( + id, + pts, + bus: false, + name: none, + name-pos: "middle", + slice: none, + color: black, + dashed: false, + style: "direct", + reverse: false, + zigzag-ratio: 50%, + dodge-y: 0, + dodge-sides: ("east", "west"), + dodge-margins: (5%, 5%) +) = draw.get-ctx(ctx => { + let styles = ( + "direct", + "zigzag", + "dodge" + ) + if not style in styles { + panic("Invalid wire style '" + style + "'") + } + if pts.len() != 2 { + panic("Wrong number of points for style '" + style + "' (got " + str(pts.len()) + " instead of 2)") + } + + let stroke = (paint: color, thickness: if bus {1.5pt} else {1pt}) + if dashed { + stroke.insert("dash", "dashed") + } + + let points = () + let anchors = () + + if style == "direct" { + points = pts + anchors = ( + "start": points.first(), + "end": points.last() + ) + + } else if style == "zigzag" { + let start = pts.at(0) + let end = pts.at(1) + let mid = (start, zigzag-ratio, end) + + points = ( + start, + (horizontal: mid, vertical: ()), + (horizontal: (), vertical: end), + end + ) + anchors = ( + "start": points.first(), + "zig": points.at(1), + "zag": points.at(1), + "end": points.last() + ) + + } else if style == "dodge" { + let start = pts.at(0) + let end = pts.at(1) + let p1 = (start, dodge-margins.at(0), end) + let p2 = (end, dodge-margins.at(1), start) + + let (ctx, p0) = coordinate.resolve(ctx, start) + let (ctx, p3) = coordinate.resolve(ctx, end) + p0 = (x: p0.at(0), y: p0.at(1)) + p3 = (x: p3.at(0), y: p3.at(1)) + let (margin-start, margin-end) = dodge-margins + let (side-start, side-end) = dodge-sides + + let dx1 = margin-start + let dx2 = margin-end + + if type(margin-start) == ratio { + dx1 = calc.abs(p3.x - p0.x) * margin-start / 100% + } + if type(margin-end) == ratio { + dx2 = calc.abs(p3.x - p0.x) * margin-end / 100% + } + if side-start == "west" { + dx1 *= -1 + } + if side-end == "east" { + dx2 *= -1 + } + p1 = (p0.x + dx1, p0.y) + p2 = (p3.x - dx2, p0.y) + + points = ( + start, + (horizontal: p1, vertical: ()), + (horizontal: (), vertical: (0, dodge-y)), + (horizontal: p2, vertical: ()), + (horizontal: (), vertical: end), + end + ) + anchors = ( + "start": start, + "start2": points.at(1), + "dodge-start": points.at(2), + "dodge-end": points.at(3), + "end2": points.at(4), + "end": end + ) + } + + draw.group(name: id, { + draw.line(..points, stroke: stroke) + for (anchor-name, anchor-pos) in anchors { + draw.anchor(anchor-name, anchor-pos) + } + }) + + let first-pt = id + ".start" + let last-pt = id + ".end" + if reverse { + (first-pt, last-pt) = (last-pt, first-pt) + } + + if name != none { + let names = () + + if type(name) == str { + names = ((name, name-pos),) + + } else if type(name) == array { + names = ( + (name.at(0), "start"), + (name.at(1), "end") + ) + } + + for (name, pos) in names { + let point + let anchor + + if pos == "middle" { + point = (first-pt, 50%, last-pt) + anchor = "south" + } else if pos == "start" { + point = first-pt + anchor = "south-west" + } else if pos == "end" { + point = last-pt + anchor = "south-east" + } + draw.content(point, anchor: anchor, padding: 3pt, name) + } + } + if slice != none { + let slice-txt = "[" + str(slice.first()) + ":" + str(slice.last()) + "]" + + draw.content( + first-pt, + anchor: "south-west", + padding: 3pt, + text(slice-txt, size: 0.75em) + ) + } +}) + +#let diag-intersection(pt) = { + draw.circle(pt, radius: .2, stroke: none, fill: black) +} + +#context { + canvas(length: 2em, { + diag-block( + x: 0, y: 0, w: 1.5, h: 2.2, + id: "PC-buf", + fill: diag-colors.orange, + ports: ( + west: ( + (id: "PCNext"), + ), + north: ( + (id: "CLK", clock: true), + ), + east: ( + (id: "PC"), + ), + south: ( + (id: "EN", name: "EN"), + ) + ) + ) + diag-port-stub("PC-buf-port-CLK", "north", name: "CLK") + diag-port-stub("PC-buf-port-EN", "south", name: "PCWrite") + + diag-multiplexer( + x: 3, y: (from: "PC-buf-port-PC", to: "in0"), w: 1, h: 2, + id: "AdrSrc-MP", + fill: diag-colors.orange, + entries: 2 + ) + diag-wire( + "wPCBuf-InstDataMgr", ( + "PC-buf-port-PC", + "AdrSrc-MP-port-in0" + ), + name: "PC", + bus: true + ) + diag-port-stub("AdrSrc-MP.north", "north", name: "AdrSrc") + + diag-block( + x: 6, y: (from: "AdrSrc-MP-port-out", to: "A"), w: 3, h: 4, + id: "InstDataMgr", + fill: diag-colors.yellow, + ports: ( + west: ( + (id: "A", name: "A"), + (id: "WD", name: "WD") + ), + north: ( + (id: "CLK", clock: true), + (id: "WE", name: "WE", vertical: true), + (id: "IRWrite", name: "IRWrite", vertical: true) + ), + east: ( + (id: "Instr", name: "Instr."), + (id: "RD", name: "RD") + ) + ), + ports-margins: ( + west: (30%, 0%), + east: (40%, 0%) + ) + ) + diag-wire( + "wAdrSrcMP-InstDataMgr", ( + "AdrSrc-MP-port-out", + "InstDataMgr-port-A" + ), + name: "Addr", + name-pos: "end", + bus: true + ) + + diag-port-stub("InstDataMgr-port-CLK", "north", name: "CLK") + diag-port-stub("InstDataMgr-port-WE", "north") + diag-port-stub("InstDataMgr-port-IRWrite", "north") + diag-port-stub("InstDataMgr-port-WD", "west") + + diag-block( + x: 15, y: (from: "InstDataMgr-port-RD", to: "WD3"), w: 3, h: 4, + id: "RegFile", + fill: diag-colors.pink, + ports: ( + west: ( + (id: "A1", name: "A1"), + (id: "A2", name: "A2"), + (id: "A3", name: "A3"), + (id: "WD3", name: "WD3"), + ), + north: ( + (id: "CLK", clock: true), + (id: "WE3", name: "WE3", vertical: true) + ), + east: ( + (id: "RD1", name: "RD1"), + (id: "RD2", name: "RD2"), + ) + ), + ports-margins: ( + east: (20%, 20%) + ) + ) + diag-port-stub("RegFile-port-CLK", "north", name: "CLK") + diag-port-stub("RegFile-port-WE3", "north", name: "Regwrite", vertical: true) + diag-port-stub("RegFile-port-A2", "west") + diag-port-stub("RegFile-port-RD2", "east") + + diag-extender( + x: 15, y: -3.5, w: 3, h: 1, + id: "Extender", + fill: diag-colors.green + ) + diag-wire( + "wExtender-ImmSrc", ( + "Extender.north", + (18, -2) + ), + style: "zigzag", + zigzag-ratio: 0%, + name: "ImmSrc", + name-pos: "end", + bus: true + ) + + let mid = ("InstDataMgr.east", 50%, "RegFile.west") + diag-wire( + "wInstDataMgr-Bus", ( + "InstDataMgr-port-Instr", + (vertical: (), horizontal: mid) + ), + name: "Instr", + name-pos: "start", + bus: true + ) + diag-wire( + "wBus", ( + (v => (v.at(0), -3.5), mid), + (horizontal: (), vertical: (0, 3.5)), + ), + bus: true + ) + diag-wire( + "wBus-RegFile-A1", ( + "RegFile-port-A1", + (horizontal: mid, vertical: ()), + ), + name: "RS1", + name-pos: "end", + slice: (19, 15), + reverse: true, + bus: true + ) + diag-wire( + "wBus-RegFile-A3", ( + "RegFile-port-A3", + (horizontal: mid, vertical: ()), + ), + name: "RD", + name-pos: "end", + slice: (11, 7), + reverse: true, + bus: true + ) + diag-wire( + "wBus-Extender", ( + "Extender-port-in", + (horizontal: mid, vertical: ()), + ), + slice: (31, 7), + reverse: true, + bus: true + ) + + diag-alu( + x: 22, y: (from: "RegFile-port-RD1", to: "in1"), w: 1, h: 2, + id: "ALU", + fill: diag-colors.purple + ) + diag-wire( + "wRegFile-ALU", ( + "RegFile-port-RD1", + "ALU-port-in1" + ), + name: ("A", "SrcA"), + bus: true + ) + + diag-block( + x: 26, y: (from: "ALU-port-out", to: "in"), w: 1.5, h: 2, + id: "OutBuf", + fill: diag-colors.orange, + ports: ( + west: ( + (id: "in"), + ), + north: ( + (id: "CLK", clock: true), + ), + east: ( + (id: "out"), + ) + ) + ) + diag-port-stub("OutBuf-port-CLK", "north", name: "CLK") + diag-wire( + "wALU-OutBuf", ( + "ALU-port-out", + "OutBuf-port-in" + ), + name: "ALUResult", + bus: true + ) + + diag-multiplexer( + x: 30, y: (from: "OutBuf-port-out", to: "in0"), w: 1, h: 2.5, + id: "Res-MP", + fill: diag-colors.orange, + entries: 3 + ) + diag-port-stub("Res-MP.north", "north", name: "ResultSrc") + diag-port-stub("Res-MP-port-in2", "west") + diag-wire( + "wOutBuf-ResMP", ( + "OutBuf-port-out", + "Res-MP-port-in0" + ), + name: "ALUOut", + bus: true + ) + + diag-wire( + "wExt-ALU", ( + "Extender-port-out", + "ALU-port-in2", + ), + name: ("ImmExt", "SrcB"), + bus: true, + style: "zigzag", + zigzag-ratio: 60% + ) + + diag-wire( + "wInstDataMgr-ResMP", ( + "InstDataMgr-port-RD", + "Res-MP-port-in1" + ), + style: "dodge", + dodge-y: -4, + dodge-sides: ("east", "west"), + name: "Data", + name-pos: "start", + bus: true + ) + + diag-wire( + "wResMP-AdrSrc", ( + "Res-MP-port-out", + "AdrSrc-MP-port-in1" + ), + style: "dodge", + dodge-y: -5, + dodge-sides: ("east", "west"), + dodge-margins: (0.5, 1), + bus: true + ) + + diag-wire( + "wResMP-RegFile", ( + "Res-MP-port-out", + "RegFile-port-WD3" + ), + style: "dodge", + dodge-y: -5, + dodge-sides: ("east", "west"), + dodge-margins: (0.5, 1), + bus: true + ) + + diag-wire( + "wResMP-PCBuf", ( + "Res-MP-port-out", + "PC-buf-port-PCNext" + ), + style: "dodge", + dodge-y: -5, + dodge-sides: ("east", "west"), + dodge-margins: (0.5, 1.5), + name: "PCNext", + name-pos: "end", + bus: true + ) + + + diag-intersection("wResMP-RegFile.dodge-end") + diag-intersection("wResMP-AdrSrc.dodge-end") + }) +} \ No newline at end of file