perilus

git clone https://git.bradleygannon.com/perilus.git
Log | Files | Refs | Submodules | README

commit 81c4e9227f1fb8c382d3db522c071a736ae843e8
parent e1885a909491dfcaebfbacdf2bc3b1b0095b3431
Author: Bradley Gannon <bradley@bradleygannon.com>
Date:   Thu,  2 Apr 2026 21:25:35 -0400

Add system diagram

Diffstat:
M.github/workflows/ci.yml | 13+++++++++++++
M.gitignore | 1+
Adoc/perilus.typ | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjustfile | 8+++++---
Mshell.nix | 1+
5 files changed, 162 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml @@ -45,6 +45,9 @@ jobs: sudo apt-get update sudo apt-get install -y verilator + - name: Install Typst + uses: typst-community/setup-typst@v5 + # Ensure sbt & scalafmt plugins are resolved (if needed) - name: sbt update run: sbt update @@ -67,8 +70,18 @@ jobs: sbt run cd generated && zip ../perilus.zip ./* + - name: Compile system diagram to SVG + run: | + typst compile --format svg doc/perilus.typ doc/perilus.svg + - name: Upload perilus.zip artifact uses: actions/upload-artifact@v4 with: name: perilus path: perilus.zip + + - name: Upload documentation SVG artifact + uses: actions/upload-artifact@v4 + with: + name: perilus-doc + path: doc/perilus.svg diff --git a/.gitignore b/.gitignore @@ -1,6 +1,7 @@ *.S *.s *.sv +*.svg *.v .bloop/ .bsp/ diff --git a/doc/perilus.typ b/doc/perilus.typ @@ -0,0 +1,142 @@ +#import "@preview/fletcher:0.5.8" as fletcher: diagram, node, edge, shapes.trapezium + +#set page(width: auto, height: auto) + +#let reg(x, y, id) = { + node((x, y), raw(id), name: id, stroke: 1pt) +} + +#let mux(x, y, id, n) = { + let i = 0 + while i < n { + node((x, {y + i}), raw(str(i)), name: id + "_" + str(i)) + i += 1 + } + let i = 0 + let enclose = while i < n { + (label(id + "_" + str(i)),) + i += 1 + } + node( + name: id, + enclose: enclose, + shape: trapezium.with(dir: right), + stroke: 1pt, + inset: 0mm, + ) +} + +#let module(x, y, id, io_ids, width, alignment) = { + let io_y = y + for io_id in io_ids { + let name = id + "_" + io_id + node((x, io_y), align(alignment)[#raw(io_id)], name: name, width: width) + io_y = io_y + 1 + } + let io_names = for io_id in io_ids { + (label(id + "_" + io_id),) + } + node( + enclose: io_names, + name: label(id), + stroke: 1pt, + inset: 0mm, + ) +} + +#diagram( + debug: 0, + spacing: (5mm, 1mm), + + module( + 24, 0, "controlUnit", + ("op", "funct3", "funct7-5", "zero", "adrSrc", "irWrite", "memWrite", "pcWrite", "regWrite", + "aluSrcA", "aluSrcB", "resultSrc", "immSrc", "aluControl", "dataMask", "lessThan"), + 23mm, + left, + ), + module(0, 0, "registerFile", ("a1", "a2", "a3", "writeData3", "writeEnable3", "rd1", "rd2"), 27mm, +right), + module(0, 7, "extendUnit", ("input", "immSrc", "immExt"), 27mm, right), + module(0, 10, "memory", ("address", "writeData", "writeEnable", "readData", "dataMask"), 27mm, right), + module(0, 15, "alu", ("aluControl", "srcA", "srcB", "aluResult", "zero", "lessThan"), 27mm, right), + + reg(12.5, 16.5, "pc"), + reg(14, 16.5, "oldPc"), + reg(12, 1, "instr"), + reg(8, 16, "readDataBuf"), + reg(8, 5, "rd1Buf"), + reg(8, 6, "rd2Buf"), + reg(8, 7, "four"), + reg(8, 15, "aluOutBuf"), + + edge(<controlUnit_pcWrite.west>, (12.5, 7), <pc.north>, "->"), + + edge(<controlUnit_irWrite.west>, (16, 5), (16, 0), (12, 0), <instr.north>, "->"), + edge(<pc.east>, <oldPc.west>, "->"), + + edge(<controlUnit_irWrite.west>, (14, 5), <oldPc.north>, "->"), + edge(<memory_readData.east>, (7, 13), (7, 1), <instr.west>, "->"), + + edge(<memory_readData.east>, (7, 13), (7, 16), <readDataBuf.west>, "->"), + + edge(<registerFile_rd1.east>, <rd1Buf.west>, "->"), + edge(<registerFile_rd2.east>, <rd2Buf.west>, "->"), + + edge(<alu_aluResult.east>, (5, 18), (5, 15), <aluOutBuf.west>, "->"), + + mux(9, 15, "muxResult", 4), + edge(<controlUnit_resultSrc.west>, (9, 11), <muxResult.north>, "->"), + edge(<muxResult.east>, (10, 16.5), <pc.west>, "->", label: `result`, label-pos: 0.5, label-side: right), + edge(<aluOutBuf.east>, <muxResult_0.west>, "->"), + edge(<readDataBuf.east>, <muxResult_1.west>, "->"), + edge(<alu_aluResult.east>, (5, 18), (5, 17), <muxResult_2.west>, "->"), + edge(<extendUnit_immExt.east>, (2, 9), (2, 17.5), (6, 17.5), (6, 18), <muxResult_3.west>, "->"), + + edge(<controlUnit_aluControl.west>, (16, 13), (16, 13.75), (3, 13.75), (3, 15), <alu_aluControl.east>, "->"), + + mux(17, 15.5, "muxAluSrcA", 3), + edge(<controlUnit_aluSrcA.west>, (17, 9), <muxAluSrcA.north>, "->"), + edge(<muxAluSrcA.east>, (18, 16.5), (18, 18.75), (3, 18.75), (3, 16), <alu_srcA.east>, "->", label: `A`, label-pos: 0.15), + edge(<pc.east>, (13, 16.5), (13, 15.5), <muxAluSrcA_0.west>, "->"), + edge(<oldPc.east>, <muxAluSrcA_1.west>, "->"), + edge(<rd1Buf.east>, (8.5, 5), (8.5, 3.5), (11.5, 3.5), (11.5, 17.5), <muxAluSrcA_2.west>, "->"), + + mux(10, 5, "muxAluSrcB", 3), + edge(<controlUnit_aluSrcB.west>, (18, 10), (18, 3), (10, 3), <muxAluSrcB.north>, "->"), + edge(<muxAluSrcB.east>, (11, 6), (11, 13.5), (4, 13.5), (4, 17), <alu_srcB.east>, "->", label: `B`, label-pos: 0.15), + edge(<rd2Buf.east>, (9, 6), (9, 5), <muxAluSrcB_0.west>, "->"), + edge(<extendUnit_immExt.east>, (9.5, 9), (9.5, 6), <muxAluSrcB_1.west>, "->"), + edge(<four.east>, <muxAluSrcB_2.west>, "->"), + + edge(<alu_zero.east>, (22, 19), (22, 3), <controlUnit_zero.west>, "->"), + edge(<alu_lessThan.east>, (23, 20), (23, 15), <controlUnit_lessThan.west>, "->"), + + edge(<instr.east>, (18, 1), (18, 0), <controlUnit_op.west>, "->", label: `[6:0]`, label-pos: 0.82), + edge(<instr.east>, (18, 1), <controlUnit_funct3.west>, "->", label: `[14:12]`, label-pos: + 0.75), + edge(<instr.east>, (18, 1), (18, 2), <controlUnit_funct7-5.west>, "->", label: `[30]`, label-pos: + 0.82), + + edge(<instr.east>, (14, 1), (14, -1), (5, -1), (5, 7), <extendUnit_input.east>, "->", label: `[31:7]`, label-pos: 0.9), + edge(<controlUnit_immSrc.west>, (21, 12), (21, 8), <extendUnit_immSrc.east>, "->"), + + mux(15, 12, "muxMemAddr", 2), + edge(<controlUnit_adrSrc.west>, (15, 4), <muxMemAddr.north>, "->"), + edge(<muxMemAddr.east>, (16, 12.5), (16, 10), <memory_address.east>, "->"), + edge(<muxResult.east>, (10, 16.5), (10, 12), <muxMemAddr_0.west>, "->"), + edge(<pc.east>, (13, 16.5), (13, 13), <muxMemAddr_1.west>, "->"), + + edge(<rd2Buf.east>, (8.5, 6), (8.5, 11), <memory_writeData.east>, "->"), + edge(<controlUnit_memWrite.west>, (14.5, 6), (14.5, 10.5), (9.5, 10.5), (9.5, 12), <memory_writeEnable.east>, "->"), + edge(<controlUnit_dataMask.west>, <memory_dataMask.east>, "->"), + + edge(<instr.east>, (14, 1), (14, -1), (5, -1), (5, 0), <registerFile_a1.east>, "->", label: + `[19:15]`, label-pos: 0.9), + edge(<instr.east>, (14, 1), (14, -1), (5, -1), (5, 1), <registerFile_a2.east>, "->", label: + `[24:20]`, label-pos: 0.9), + edge(<instr.east>, (14, 1), (14, -1), (5, -1), (5, 2), <registerFile_a3.east>, "->", label: + `[11:7]`, label-pos: 0.9), + edge(<muxResult.east>, (10, 16.5), (10, 9.5), (4, 9.5), (4, 3), <registerFile_writeData3.east>, "->"), + edge(<controlUnit_regWrite.west>, (23, 8), (23, 4.5), (11, 4.5), (11, 4), <registerFile_writeEnable3.east>, "->"), +) diff --git a/justfile b/justfile @@ -1,10 +1,13 @@ -ci: fmt clean test +ci: fmt clean test doc test: sbt test +doc: + typst compile -f svg doc/perilus.typ + fmt: sbt scalafmtAll clean: - sbt clean- \ No newline at end of file + sbt clean diff --git a/shell.nix b/shell.nix @@ -13,6 +13,7 @@ pkgs.mkShellNoCC { llvm metals sbt + typst verilator ]; }