sourcelibs/Clocked/src/Clocked.xtl

1⍝# Clocked: clocked circuits as X_eTaL step functions: counters, accumulators, shift registers, a sequence detector, a RAM, and the harness that clocks them 2⍝# Import it with an alias of your choice: "ck:" u_se< "Clocked". 3⍝# Put libs/Clocked/src on XETAL_PATH ("just path"); the reference is libs/Clocked/docs. 4⍝# Names with l: are exported; the others are private to this file. 5⍝# 6⍝# A clocked circuit is state' = step(state, input) at every clock. 7⍝# Here a step is an ordinary pure dyadic function, s f̲ x: the state 8⍝# on the left, one clock's input on the right, the next state out. That 9⍝# function is what becomes hardware: registers for the state, the 10⍝# function as the logic between them. The harness, r_un and t_race, 11⍝# applies it once per input, as a clock would: a loop, p_ower over a 12⍝# tuple of the clock count and the state (X_eTaL has no left fold yet, 13⍝# ask F5). It is the testbench, never lowered. Numbers are 8-bit two's complement (Fixed). 14ᶠˣ⁼u̲se< "Fixed" 15 16⍝## The harness: one input per clock 17 18⍝# s0 'step r_un xs: the state after every input (an item, or a row of 19⍝# a matrix, per clock). 20⍝# >> "ck:" u_se< "Clocked" 21⍝# >> 0 'ck:a_ccumulate ck:r_un 1 2 3 4 22⍝# 10 23ˡr̲un ← { f̲ s xs → 24 (n, last) ← (t̲ally xs) '{ (i, t) → (i + 1, t f̲ i s̲elect xs) } p̲ower (1, s) 25 last 26} 27 28⍝# s0 'step t_race xs: every state from s0 on, one per row (a vector 29⍝# for a scalar state): what the registers hold after reset and after 30⍝# each clock. 31⍝# >> "ck:" u_se< "Clocked" 32⍝# >> (0 0 0) 'ck:s_hift ck:t_race 1 1 0 33⍝# 0 0 0 34⍝# 0 0 1 35⍝# 0 1 1 36⍝# 1 1 0 37ˡt̲race ← { f̲ s xs → 38 first ← (1 c̲at s̲hape s) r̲eshape s 39 (n, last, rows) ← (t̲ally xs) '{ (i, t, m) → t1 ← t f̲ i s̲elect xs◆ (i + 1, t1, m c̲at t1) } p̲ower (1, s, first) 40 rows 41} 42 43⍝## Counters and accumulators 44 45⍝# s t_ick e: an 8-bit counter that counts when its enable e is 1. 46⍝# >> "ck:" u_se< "Clocked" 47⍝# >> 127 ck:t_ick 1 48⍝# -128 49ˡt̲ick ← { s e → 8 ᶠˣw̲rap s + e } 50 51⍝# s a_ccumulate x: an 8-bit accumulator (an adder and a register). 52⍝# >> "ck:" u_se< "Clocked" 53⍝# >> 100 ck:a_ccumulate 100 54⍝# -56 55ˡa̲ccumulate ← { s x → 8 ᶠˣw̲rap s + x } 56 57⍝## Shift register 58 59⍝# s s_hift b: the bits move one place toward the front and b comes in 60⍝# at the end (a serial-in, parallel-out shift register: wiring and 61⍝# flip-flops, no logic). 62⍝# >> "ck:" u_se< "Clocked" 63⍝# >> (0 0 0 1) ck:s_hift 1 64⍝# 0 0 1 1 65ˡs̲hift ← { s b → (1 d̲rop s) c̲at b } 66 67⍝## A sequence detector: a state machine from a table 68 69⍝# Next101: the next state of a detector of the bits 1 0 1 (overlapping), 70⍝# a table indexed by state and bit: item 1 + s + 4 * b. States: 0 71⍝# nothing yet, 1 seen 1, 2 seen 1 0, 3 seen 1 0 1 (found). In 72⍝# hardware the table is a ROM or a few gates. 73⍝# >> "ck:" u_se< "Clocked" 74⍝# >> ck:Next101 75⍝# 0 2 0 2 1 1 3 1 76ˡNext101 ← 0 2 0 2 1 1 3 1 77 78⍝# s d_etect b: the detector's next state. 79⍝# >> "ck:" u_se< "Clocked" 80⍝# >> 2 ck:d_etect 1 81⍝# 3 82ˡd̲etect ← { s b → (1 + s + 4 × b) s̲elect ˡNext101 } 83 84⍝# f_ound? s: whether the state says 1 0 1 was just seen (the Moore 85⍝# output). 86⍝# >> "ck:" u_se< "Clocked" 87⍝# >> ck:f_ound? 0 1 2 3 88⍝# 0 0 0 1 89ˡf̲ound? ← { s → s = 3 } 90 91⍝## A RAM 92 93⍝# m w_rite c: a memory of 8-bit words after one clock of a write port: 94⍝# the command c is write-enable, address (from 0) and data; when 95⍝# write-enable is 1 the word at the address becomes the data, every 96⍝# other word is kept (each word a register with a mux in front). 97⍝# >> "ck:" u_se< "Clocked" 98⍝# >> (4 r_eshape 0) ck:w_rite 1 2 9 99⍝# 0 0 9 0 100ˡw̲rite ← { m c → 101 we ← f̲irst c 102 hit ← (o̲ffsets t̲ally m) = 2 s̲elect c 103 8 ᶠˣw̲rap m + we × hit × (3 s̲elect c) − m 104} 105 106⍝# m r_ead a: the word at address a (from 0): a read port, a mux. 107⍝# >> "ck:" u_se< "Clocked" 108⍝# >> (0 0 9 0) ck:r_ead 2 109⍝# 9 110ˡr̲ead ← { m a → (1 + a) s̲elect m }