diff --git a/doc/examples.typ b/doc/examples.typ index 423a3c9..ba606cb 100644 --- a/doc/examples.typ +++ b/doc/examples.typ @@ -113,4 +113,31 @@ gates.gate-xor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all") #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) \ No newline at end of file +```, 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") +```) \ No newline at end of file diff --git a/gallery/test.pdf b/gallery/test.pdf index 8539fc3..18778e4 100644 Binary files a/gallery/test.pdf and b/gallery/test.pdf differ diff --git a/gallery/test4.pdf b/gallery/test4.pdf new file mode 100644 index 0000000..d16be3c Binary files /dev/null and b/gallery/test4.pdf differ diff --git a/gallery/test4.typ b/gallery/test4.typ new file mode 100644 index 0000000..bfb96c8 --- /dev/null +++ b/gallery/test4.typ @@ -0,0 +1,217 @@ +#import "@preview/cetz:0.2.2": draw +#import "../src/lib.typ": * + +#set page(flipped: true) + +#circuit({ + element.group(id: "toplvl", name: "Toplevel", { + element.group( + id: "proc", + name: "Processor", + padding: 1.5em, + stroke: (dash: "dashed"), + { + element.block( + x: 0, y: 0, w: 8, h: 4, + id: "dp", + fill: util.colors.pink, + name: "Datapath", + ports: ( + north: ( + (id: "clk", clock: true), + (id: "Zero"), + (id: "Regsrc"), + (id: "PCSrc"), + (id: "ResultSrc"), + (id: "ALUControl"), + (id: "ImmSrc"), + (id: "RegWrite"), + (id: "dummy") + ), + east: ( + (id: "PC", name: "PC"), + (id: "Instr", name: "Instr"), + (id: "ALUResult", name: "ALUResult"), + (id: "dummy"), + (id: "WriteData", name: "WriteData"), + (id: "ReadData", name: "ReadData"), + ), + west: ( + (id: "rst"), + ) + ), + ports-margins: ( + north: (0%, 0%), + west: (0%, 70%) + ) + ) + + element.block( + x: 0, y: 7, w: 8, h: 3, + id: "ctrl", + fill: util.colors.orange, + name: "Controller", + ports: ( + east: ( + (id: "Instr", name: "Instr"), + ), + south: ( + (id: "dummy"), + (id: "Zero"), + (id: "Regsrc"), + (id: "PCSrc"), + (id: "ResultSrc"), + (id: "ALUControl"), + (id: "ImmSrc"), + (id: "RegWrite"), + (id: "MemWrite") + ) + ), + ports-margins: ( + south: (0%, 0%) + ) + ) + wire.wire( + "w-Zero", + ("dp-port-Zero", "ctrl-port-Zero"), + name: "Zero", + name-pos: "start", + directed: true + ) + for p in ("Regsrc", "PCSrc", "ResultSrc", "ALUControl", "ImmSrc", "RegWrite") { + wire.wire( + "w-" + p, + ("ctrl-port-"+p, "dp-port-"+p), + name: p, + name-pos: "start", + directed: true + ) + } + + draw.content( + (rel: (0, 1em), to: "ctrl.north"), + [*RISCV single*], + anchor: "south" + ) + }) + + element.block( + x: (rel: 3.5, to: "dp.east"), + y: (from: "dp-port-ReadData", to: "RD"), + w: 3, h: 4, + id: "dmem", + fill: util.colors.green, + name: "Data\n Memory", + ports: ( + north: ( + (id: "clk", clock: true), + (id: "WE", name: "WE") + ), + west: ( + (id: "dummy"), + (id: "dummy"), + (id: "A", name: "A"), + (id: "dummy"), + (id: "WD", name: "WD"), + (id: "RD", name: "RD"), + ) + ), + ports-margins: ( + north: (0%, 10%) + ) + ) + wire.wire( + "w-DataAddr", + ("dp-port-ALUResult", "dmem-port-A"), + name: "DataAddr", + name-pos: "end", + directed: true + ) + wire.wire( + "w-WriteData", + ("dp-port-WriteData", "dmem-port-WD"), + name: "WriteData", + name-pos: "end", + directed: true + ) + wire.wire( + "w-ReadData", + ("dmem-port-RD", "dp-port-ReadData"), + name: "ReadData", + name-pos: "end", + reverse: true, + directed: true + ) + wire.wire( + "w-MemWrite", + ("ctrl-port-MemWrite", "dmem-port-WE"), + style: "zigzag", + name: "MemWrite", + name-pos: "start", + zigzag-dir: "horizontal", + zigzag-ratio: 80%, + directed: true + ) + wire.stub( + "dmem-port-clk", "north", + name: "clk", length: 3pt + ) + + element.block( + x: (rel: 3.5, to: "dp.east"), + y: (from: "ctrl-port-Instr", to: "dummy"), + w: 3, h: 4, + id: "imem", + fill: util.colors.green, + name: "Instruction\n Memory", + ports: ( + west: ( + (id: "A", name: "A"), + (id: "dummy"), + (id: "dummy2"), + (id: "RD", name: "RD"), + ) + ) + ) + wire.wire( + "w-PC", + ("dp-port-PC", "imem-port-A"), + style: "zigzag", + directed: true + ) + wire.wire( + "w-Instr1", + ("imem-port-RD", "dp-port-Instr"), + style: "zigzag", + zigzag-ratio: 30%, + directed: true + ) + wire.wire( + "w-Instr2", + ("imem-port-RD", "ctrl-port-Instr"), + style: "zigzag", + zigzag-ratio: 30%, + directed: true + ) + wire.intersection("w-Instr1.zig", radius: 2pt) + draw.content("w-Instr1.zig", "Instr", anchor: "south", padding: 4pt) + draw.content("w-PC.zig", "PC", anchor: "south-east", padding: 2pt) + + draw.content("dmem.south-west", [*External Memories*], anchor: "north", padding: 10pt) + }) + + wire.wire( + "w-dp-clk", + ("dp-port-clk", (-1, 4.2)), + style: "zigzag", + zigzag-dir: "horizontal", + zigzag-ratio: 100% + ) + draw.content("w-dp-clk.end", "clk", anchor: "east", padding: 3pt) + + wire.wire( + "w-dp-rst", + ("dp-port-rst", (horizontal: (-1, 0), vertical: ())) + ) + draw.content("w-dp-rst.end", "rst", anchor: "east", padding: 3pt) +}) diff --git a/manual.pdf b/manual.pdf index 2c0c728..241280f 100644 Binary files a/manual.pdf and b/manual.pdf differ diff --git a/manual.typ b/manual.typ index 11ae7d2..7d1ef53 100644 --- a/manual.typ +++ b/manual.typ @@ -158,7 +158,8 @@ Simply import #link("src/lib.typ") and call the `circuit` function: read("src/elements/alu.typ") + "\n" + read("src/elements/block.typ") + "\n" + read("src/elements/extender.typ") + "\n" + - read("src/elements/multiplexer.typ"), + read("src/elements/multiplexer.typ") + "\n" + + read("src/elements/group.typ"), name: "element", scope: ( element: element, diff --git a/src/element.typ b/src/element.typ index 69bf876..3bf0e17 100644 --- a/src/element.typ +++ b/src/element.typ @@ -10,4 +10,6 @@ #import "elements/logic/and.typ": gate-and, gate-nand #import "elements/logic/or.typ": gate-or, gate-nor #import "elements/logic/xor.typ": gate-xor, gate-xnor -#import "elements/logic/buf.typ": gate-buf, gate-not \ No newline at end of file +#import "elements/logic/buf.typ": gate-buf, gate-not + +#import "elements/group.typ": group \ No newline at end of file diff --git a/src/elements/element.typ b/src/elements/element.typ index 1822f93..5b6e31e 100644 --- a/src/elements/element.typ +++ b/src/elements/element.typ @@ -33,7 +33,7 @@ /// - `name` (`str`): Optional name displayed *in* the block /// - `clock` (`bool`): Whether it is a clock port (triangle symbol) /// - `vertical` (`bool`): Whether the name should be drawn vertically -/// - ports-margins (dictionary): Dictionary of ports margins (used with automatic port placement). They keys are cardinal directions ("north", "east", "south", "west"). The values are tuples of (, ) margins (numbers) +/// - ports-margins (dictionary): Dictionary of ports margins (used with automatic port placement). They keys are cardinal directions ("north", "east", "south", "west"). The values are tuples of (``, ``) margins (numbers) /// - fill (none, color): Fill color /// - stroke (stroke): Border stroke /// - id (str): The block id (for future reference) diff --git a/src/elements/group.typ b/src/elements/group.typ new file mode 100644 index 0000000..5a12f3f --- /dev/null +++ b/src/elements/group.typ @@ -0,0 +1,80 @@ +#import "@preview/cetz:0.2.2": draw, coordinate +#import "../util.typ" + +/// Draws a group of elements +/// +/// #examples.group +/// - body (elements, function): Elements to group +/// - id (str): see #doc-ref("element.elmt") +/// - name (str): The group's name +/// - name-anchor (str): The anchor for the name. \ +/// Note: the name will be placed on the *outside* of the group +/// - fill (color): see #doc-ref("element.elmt") +/// - stroke (stroke): see #doc-ref("element.elmt") +/// - padding (float,length,array,dictionary): The inside padding: +/// - float / length: same for all sides +/// - array: either (``,), (``, ``) or (``, ``, ``, ``) +/// - dictionary: valid keys are "top", "right", "bottom" and "left" +/// - radius (number): The corner radius +#let group( + body, + id: "", + name: none, + name-anchor: "south", + fill: none, + stroke: black + 1pt, + padding: 0.5em, + radius: 0.5em +) = { + let min-x = none + let max-x = none + let min-y = none + let max-y = none + + let pad-top = padding + let pad-bottom = padding + let pad-left = padding + let pad-right = padding + + if type(padding) == array { + if padding.len() == 0 { + panic("Padding array must contain at least one value") + } else if padding.len() == 1 { + pad-top = padding.first() + pad-bottom = padding.first() + pad-left = padding.first() + pad-right = padding.first() + } else if padding.len() == 2 { + pad-top = padding.first() + pad-bottom = padding.first() + pad-left = padding.last() + pad-right = padding.last() + } else if padding.len() == 4 { + (pad-top, pad-right, pad-bottom, pad-left) = padding + } + } else if type(padding) == dictionary { + pad-top = padding.at("top", default: 0.5em) + pad-right = padding.at("right", default: 0.5em) + pad-bottom = padding.at("bottom", default: 0.5em) + pad-left = padding.at("left", default: 0.5em) + } + + draw.hide(draw.group(name: id+"-inner", body)) + draw.rect( + (rel: (-pad-left, -pad-bottom), to: id+"-inner.south-west"), + (rel: (pad-right, pad-top), to: id+"-inner.north-east"), + name: id, + radius: radius, + stroke: stroke, + fill: fill + ) + if name != none { + draw.content( + id + "." + name-anchor, + anchor: util.opposite-anchor(name-anchor), + padding: 5pt, + [*#name*] + ) + } + body +} \ No newline at end of file