Skip to content

Commit

Permalink
Improve timing diagrams slightly
Browse files Browse the repository at this point in the history
  • Loading branch information
Gekkio committed Jul 28, 2024
1 parent 241b028 commit edc1fe5
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 103 deletions.
10 changes: 5 additions & 5 deletions appendix/external-bus.typ
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
]

#let bus-diagram = (addr: array, rd: array, wr: array, a15: array, cs: array, data: array, sampling-edge: false) => {
import timing: *
import timing: diagram, clock as c, data as d, either as e, high as h, low as l, unknown as u, undefined as x, high_impedance as z
text(13pt,
diagram(
grid: true,
Expand Down Expand Up @@ -40,7 +40,7 @@

#figure(
{
import timing: *
import timing: diagram, clock as c, data as d, either as e, high as h, low as l, unknown as u, undefined as x, high_impedance as z
bus-diagram(
addr: (u(10),),
rd: (e(1), l(9),),
Expand All @@ -57,7 +57,7 @@

#figure(
{
import timing: *
import timing: diagram, clock as c, data as d, either as e, high as h, low as l, unknown as u, undefined as x, high_impedance as z
[
#columns(2, [
#block(
Expand Down Expand Up @@ -120,7 +120,7 @@
)
#figure(
{
import timing: *
import timing: diagram, clock as c, data as d, either as e, high as h, low as l, unknown as u, undefined as x, high_impedance as z
[
#columns(2, [
#block(
Expand Down Expand Up @@ -182,7 +182,7 @@

#figure(
{
import timing: *
import timing: diagram, clock as c, data as d, either as e, high as h, low as l, unknown as u, undefined as x, high_impedance as z
[
#columns(2, [
#block(
Expand Down
20 changes: 10 additions & 10 deletions chapter/cpu/instruction-set.typ
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
]

#let simple-instruction-timing(mnemonic: str, timing_data: dictionary) = timing.diagram(w_scale: 0.9, ..{
import timing: *
import timing: diagram, clock as c, data as d, either as e, high as h, low as l, unknown as u, undefined as x, high_impedance as z
let duration = timing_data.duration
(
(label: "M-cycle", wave: (
Expand All @@ -29,22 +29,22 @@
x(1),
..timing_data.mem_rw.enumerate().map(((idx, label)) => {
let m_cycle = idx + 2
if label == "U" { timing.u(8) } else { timing.d(8, label) }
if label == "U" { timing.unknown(8) } else { timing.data(8, label) }
}),
x(1, opacity: 40%),
)),
)
})

#let instruction-timing(mnemonic: str, timing_data: dictionary) = timing.diagram(w_scale: 0.9, ..{
import timing: *
import timing: diagram, clock as c, data as d, either as e, high as h, low as l, unknown as u, undefined as x, high_impedance as z
let duration = timing_data.duration
let map_cycle_labels(data) = data.enumerate().map(((idx, label)) => {
let m_cycle = idx + 2
if label == "U" {
timing.u(8)
timing.unknown(8)
} else {
timing.d(8, label)
timing.data(8, label)
}
})
(
Expand All @@ -62,31 +62,31 @@
)),
(label: "Addr bus", wave: (
x(1),
timing.d(8, [Previous], opacity: 40%),
timing.data(8, [Previous], opacity: 40%),
..map_cycle_labels(timing_data.addr),
x(1, opacity: 40%),
)),
(label: "Data bus", wave: (
x(1),
timing.d(8, [IR ← mem], opacity: 40%),
timing.data(8, [IR ← mem], opacity: 40%),
..map_cycle_labels(timing_data.data),
x(1, opacity: 40%),
)),
(label: "IDU op", wave: (
x(1),
timing.d(8, [Previous], opacity: 40%),
timing.data(8, [Previous], opacity: 40%),
..map_cycle_labels(timing_data.idu_op),
x(1, opacity: 40%),
)),
(label: "ALU op", wave: (
x(1),
timing.d(8, [Previous], opacity: 40%),
timing.data(8, [Previous], opacity: 40%),
..map_cycle_labels(timing_data.alu_op),
x(1, opacity: 40%),
)),
(label: "Misc op", wave: (
x(1),
timing.d(8, [Previous], opacity: 40%),
timing.data(8, [Previous], opacity: 40%),
..map_cycle_labels(timing_data.misc_op),
x(1, opacity: 40%),
)),
Expand Down
2 changes: 1 addition & 1 deletion chapter/cpu/timing.typ
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ The following timing diagram shows all memory operations done by the CPU, and
the fetch and execute stages of each instruction:

#figure({
import timing: *
import timing: diagram, clock as c, data as d, either as e, high as h, low as l, unknown as u, undefined as x, high_impedance as z
diagram(
w_scale: 0.6,
(label: "CLK 4 MiHz", wave: (
Expand Down
182 changes: 95 additions & 87 deletions timing.typ
Original file line number Diff line number Diff line change
@@ -1,130 +1,139 @@
#import "common.typ": cetz, monotext, hex

#let c = (len, ..args) => (type: "C", len: len, ..args.named())
#let d = (len, label, ..args) => (type: "D", len: len, label: label, ..args.named())
#let e = (len, ..args) => (type: "E", len: len, ..args.named())
#let h = (len, ..args) => (type: "H", len: len, ..args.named())
#let l = (len, ..args) => (type: "L", len: len, ..args.named())
#let u = (len, ..args) => (type: "U", len: len, ..args.named())
#let x = (len, ..args) => (type: "X", len: len, ..args.named())
#let z = (len, ..args) => (type: "Z", len: len, ..args.named())
#let clock = (len, ..args) => (type: "C", len: len, ..args.named())
#let data = (len, label, ..args) => (type: "D", len: len, label: label, ..args.named())
#let either = (len, ..args) => (type: "E", len: len, ..args.named())
#let high = (len, ..args) => (type: "H", len: len, ..args.named())
#let low = (len, ..args) => (type: "L", len: len, ..args.named())
#let unknown = (len, ..args) => (type: "U", len: len, ..args.named())
#let undefined = (len, ..args) => (type: "X", len: len, ..args.named())
#let high_impedance = (len, ..args) => (type: "Z", len: len, ..args.named())

#let diagram = (..args, w_scale: 1.0, y_scale: 1.0, grid: false, fg: () => none) => {
cetz.canvas(length: 0.7em, {
import cetz.draw
let x_slope = 0.15 * w_scale;
let y_step = 2 * y_scale
let y_h = 1.0 * y_scale
let y_m = 0.5 * y_scale
let y_l = 0.0 * y_scale

let y_level = (H: 1.0 * y_scale, L: 0.0 * y_scale, M: 0.5 * y_scale)
draw.set-style(stroke: (thickness: 0.07em))

let resolve_level = (prev_state, event) => if event.type == "C" {
if prev_state.level == "H" {
"L"
} else if prev_state.level == "L" {
"H"
}
if prev_state.level == "L" { "H" } else { "L" }
} else if ("L", "H", "E").contains(event.type) {
event.type
} else if ("Z", "X", "D", "U").contains(event.type) {
"M"
}

let invert_level = (level) => if level == "L" { "H" }
else if level == "H" { "L" }
else { level }

let draw_event = (prev_state, event, next_type) => {
let x_start = if ("D", "U").contains(event.type) and prev_state.type != "" { x_slope } else { 0.0 }
let x_end = if ("D", "U").contains(next_type) {
event.len * w_scale + x_slope
} else {
event.len * w_scale
}
if event.type == "L" {
if prev_state.level == "H" {
draw.line((x_start, y_h), (x_start + x_slope, y_l), (x_end, y_l))
let opacity = 100% - (if "opacity" in event { event.opacity } else { 100% })
let fg_paint = (if event.type == "X" { red }
else if event.type == "Z" { blue }
else { black }
).lighten(opacity)
let fg_opts = (stroke: if "stroke" in event { event.stroke } else { (paint: fg_paint) })

let x_start = if ("D", "U").contains(prev_state.type) { x_slope } else { 0.0 }
let x_end = event.len * w_scale
if event.type == "L" or event.type == "H" {
let y = y_level.at(event.type)
let y_invert = y_level.at(invert_level(event.type))
if prev_state.level == event.type or prev_state.level == "" {
draw.line((x_start, y), (x_end, y), ..fg_opts)
} else if prev_state.level == "E" {
draw.line((x_start, y_h), (x_start + x_slope, y_l))
draw.line((x_start, y_l), (x_end, y_l))
draw.line((x_start, y_invert), (x_start + x_slope, y), ..fg_opts)
draw.line((x_start, y), (x_end, y), ..fg_opts)
} else if prev_state.level == "M" {
draw.line((x_start, y_m), (x_start + x_slope, y_l), (x_end, y_l))
} else {
draw.line((x_start, y_l), (x_end, y_l))
draw.line((x_start, y_level.M), (x_start + x_slope, y), (x_end, y), ..fg_opts)
} else if prev_state.level == invert_level(event.type) {
draw.line((x_start, y_invert), (x_start + x_slope, y), (x_end, y), ..fg_opts)
}
} else if event.type == "H" {
if prev_state.level == "L" {
draw.line((x_start, y_l), (x_start + x_slope, y_h), (x_end, y_h))
} else if event.type == "C" {
if prev_state.level == "L" or prev_state.level == "H" {
let prev_y = y_level.at(prev_state.level)
let y = y_level.at(invert_level(prev_state.level))
draw.line((x_start, prev_y), (x_start, y), (x_end, y), ..fg_opts)
} else if prev_state.level == "E" {
draw.line((x_start, y_l), (x_start + x_slope, y_h))
draw.line((x_start, y_h), (x_end, y_h))
draw.line((x_start, y_level.H), (x_start + x_slope, y_level.L), ..fg_opts)
draw.line((x_start, y_level.L), (x_end, y_level.L), ..fg_opts)
} else if prev_state.level == "M" {
draw.line((x_start, y_m), (x_start + x_slope, y_h), (x_end, y_h))
} else {
draw.line((x_start, y_h), (x_end, y_h))
}
} else if event.type == "C" {
if prev_state.level == "L" {
draw.line((x_start, y_l), (x_start, y_scale), (x_end, y_scale))
} else if prev_state.level == "H" {
draw.line((x_start, y_scale), (x_start, y_l), (x_end, y_l))
draw.line((x_start, y_level.M), (x_start + x_slope, y_level.L), (x_end, y_level.L), ..fg_opts)
} else if prev_state.level == "" {
draw.line((x_start, y_level.L), (x_end, y_level.L), ..fg_opts)
}
} else if event.type == "E" {
if prev_state.level == "L" {
draw.line((x_start, y_l), (x_start + x_slope, y_h), (x_end, y_h))
} else if prev_state.level == "M" {
draw.line((x_start, y_m), (x_start + x_slope, y_h), (x_end, y_h))
if prev_state.level == "L" or prev_state.level == "M" {
let y = y_level.at(prev_state.level)
draw.line((x_start, y), (x_start + x_slope, y_level.H), (x_end, y_level.H), ..fg_opts)
} else {
draw.line((x_start, y_h), (x_end, y_h))
draw.line((x_start, y_level.H), (x_end, y_level.H), ..fg_opts)
}
if prev_state.level == "H" {
draw.line((x_start, y_h), (x_start + x_slope, y_l), (x_end, y_l))
} else if prev_state.level == "M" {
draw.line((x_start, y_m), (x_start + x_slope, y_l), (x_end, y_l))
if prev_state.level == "H" or prev_state.level == "M" {
let y = y_level.at(prev_state.level)
draw.line((x_start, y), (x_start + x_slope, y_level.L), (x_end, y_level.L), ..fg_opts)
} else {
draw.line((x_start, y_l), (x_end, y_l))
draw.line((x_start, y_level.L), (x_end, y_level.L), ..fg_opts)
}
} else if event.type == "X" or event.type == "Z" {
if prev_state.level == "L" or prev_state.level == "H" {
let y = y_level.at(prev_state.level)
draw.line((x_start, y), (x_start + x_slope, y_level.M), (x_end, y_level.M), ..fg_opts)
} else if prev_state.level == "M" or prev_state.level == "" {
draw.line((x_start, y_level.M), (x_end, y_level.M), ..fg_opts)
} else if prev_state.level == "E" {
draw.line((x_start, y_level.H), (x_start + x_slope, y_level.M))
draw.line((x_start, y_level.L), (x_start + x_slope, y_level.M))
draw.line((x_start + x_slope, y_level.M), (x_end, y_level.M), ..fg_opts)
}
} else if event.type == "X" {
let opacity = 100% - (if "opacity" in event { event.opacity } else { 100% })
draw.line((x_start, y_m), (x_end, y_m), stroke: (paint: red.lighten(opacity)))
} else if event.type == "Z" {
let opacity = 100% - (if "opacity" in event { event.opacity } else { 100% })
draw.line((x_start, y_m), (x_end, y_m), stroke: (paint: blue.lighten(opacity)))
} else if event.type == "D" or event.type == "U" {
let opacity = 100% - (if "opacity" in event { event.opacity } else { 100% })
let fg = (if "stroke" in event { event.stroke } else { black }).lighten(opacity)
let x_start = if ("X", "Z", "").contains(prev_state.type) { 0.0 } else { x_slope }
let x_end = event.len * w_scale + x_slope
let fill = if event.type == "U" { gray } else if "fill" in event { event.fill.lighten(opacity) } else { none }
let left-open = prev_state.level == ""
let right-open = next_type == ""
if left-open and right-open {
if fill != none {
draw.rect((x_start, y_l), (x_end, y_h), stroke: none, fill: fill)
draw.rect((x_start, y_level.L), (x_end, y_level.H), stroke: none, fill: fill)
}
draw.line((x_start, y_h), (x_end, y_h), stroke: (paint: fg))
draw.line((x_start, y_l), (x_end, y_l), stroke: (paint: fg))
draw.line((x_start, y_level.H), (x_end, y_level.H), ..fg_opts)
draw.line((x_start, y_level.L), (x_end, y_level.L), ..fg_opts)
} else {
if prev_state.level == "H" or prev_state.level == "E" {
draw.line((0.0, y_level.H), (x_slope, y_level.M), ..fg_opts)
}
if prev_state.level == "L" or prev_state.level == "E" {
draw.line((0.0, y_level.L), (x_slope, y_level.M), ..fg_opts)
}
let points = ()
if left-open {
points.push((x_start, y_h))
points.push((x_end - x_slope, y_h))
points.push((x_end, y_m))
points.push((x_end - x_slope, y_l))
points.push((x_start, y_l))
points.push((x_start, y_level.H))
points.push((x_end - x_slope, y_level.H))
points.push((x_end, y_level.M))
points.push((x_end - x_slope, y_level.L))
points.push((x_start, y_level.L))
} else if right-open {
points.push((x_end, y_h))
points.push((x_start + x_slope, y_h))
points.push((x_start, y_m))
points.push((x_start + x_slope, y_l))
points.push((x_end, y_l))
points.push((x_end, y_level.H))
points.push((x_start + x_slope, y_level.H))
points.push((x_start, y_level.M))
points.push((x_start + x_slope, y_level.L))
points.push((x_end, y_level.L))
} else {
points.push((x_start, y_m))
points.push((x_start + x_slope, y_h))
points.push((x_end - x_slope, y_h))
points.push((x_end, y_m))
points.push((x_end - x_slope, y_l))
points.push((x_start + x_slope, y_l))
points.push((x_start, y_m))
points.push((x_start, y_level.M))
points.push((x_start + x_slope, y_level.H))
points.push((x_end - x_slope, y_level.H))
points.push((x_end, y_level.M))
points.push((x_end - x_slope, y_level.L))
points.push((x_start + x_slope, y_level.L))
points.push((x_start, y_level.M))
}
draw.line(..points, stroke: (paint: fg), fill: fill)
draw.line(..points, ..fg_opts, fill: fill)
}
draw.anchor("center", (event.len * w_scale / 2.0, y_m))
draw.anchor("center", ((x_end - x_start) / 2.0 + x_start, y_level.M))
if "label" in event {
draw.content("center", text(0.5em, fill: black.lighten(opacity), event.label))
}
Expand All @@ -146,7 +155,7 @@
for i in range(0, lanes.len()) {
let lane = lanes.at(i)
draw.group(ctx => {
draw.anchor("west", (0.0, y_l))
draw.anchor("west", (0.0, y_level.L))
let prev_state = (level: "", type: "")
for i in range(lane.wave.len()) {
let event = lane.wave.at(i)
Expand All @@ -155,7 +164,7 @@
prev_state.level = resolve_level(prev_state, event)
prev_state.type = event.type
}
draw.anchor("east", (0.0, y_h))
draw.anchor("east", (0.0, y_level.H))
if grid {
draw.on-layer(-1, {
draw.grid("west", "east", step: (x: 0.5 * w_scale, y: 0.5 * y_scale), stroke: (paint: gray.lighten(60%), thickness: 0.01em))
Expand All @@ -169,4 +178,3 @@
})
})
}

0 comments on commit edc1fe5

Please sign in to comment.