diff --git a/appendix/external-bus.typ b/appendix/external-bus.typ index 57b83f3..bf0a4d1 100644 --- a/appendix/external-bus.typ +++ b/appendix/external-bus.typ @@ -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, @@ -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),), @@ -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( @@ -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( @@ -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( diff --git a/chapter/cpu/instruction-set.typ b/chapter/cpu/instruction-set.typ index 727e674..f804d0f 100644 --- a/chapter/cpu/instruction-set.typ +++ b/chapter/cpu/instruction-set.typ @@ -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: ( @@ -29,7 +29,7 @@ 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%), )), @@ -37,14 +37,14 @@ }) #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) } }) ( @@ -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%), )), diff --git a/chapter/cpu/timing.typ b/chapter/cpu/timing.typ index df3e5d9..bacc55e 100644 --- a/chapter/cpu/timing.typ +++ b/chapter/cpu/timing.typ @@ -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: ( diff --git a/timing.typ b/timing.typ index 2718e8d..6c1e5b4 100644 --- a/timing.typ +++ b/timing.typ @@ -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)) } @@ -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) @@ -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)) @@ -169,4 +178,3 @@ }) }) } -