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 }