sourcelibs/Uart/src/Uart.xtl
1⍝# Uart: a serial port (UART: 8 data bits, no parity, 1 stop bit) as X_eTaL step functions: a transmitter, a receiver, and the two looped back
2⍝# Import it with an alias of your choice: "ua:" u_se< "Uart".
3⍝# Put libs/Uart/src on XETAL_PATH ("just path"); the reference is libs/Uart/docs.
4⍝# Names with l: are exported; the others are private to this file.
5⍝#
6⍝# A line idles at 1. A byte is sent as a frame of ten bits, each held
7⍝# for d clocks: a start bit (0), the eight data bits, least significant
8⍝# first, and a stop bit (1). d, the clocks per bit, is the clock rate
9⍝# over the baud rate: 234 for a 27 MHz Tang Nano at 115200 baud, 3 in
10⍝# the tests. It is the first argument of every step, bound once:
11⍝# u:t_x := ua:t_x 3, then s0 'u:t_x ck:r_un commands (Clocked's harness).
12⍝# Each step is pure, one clock's input in, the next state out.
13
14⍝## The transmitter
15
16⍝# Its state, four registers: busy (0 or 1), clocks left in this bit,
17⍝# bits left in the frame, the frame (bit 0 on the line).
18⍝# >> "ua:" u_se< "Uart"
19⍝# >> ua:TxIdle
20⍝# 0 0 0 0
21ˡTxIdle ← 0 0 0 0
22
23⍝# d t_x s c: the transmitter after one clock. A command c of 0 to 255
24⍝# starts sending that byte when it is idle; -1 is no command (a
25⍝# command while busy is ignored).
26⍝# >> "ua:" u_se< "Uart"
27⍝# >> u:t_x := ua:t_x 3
28⍝# >> ua:TxIdle u:t_x 65
29⍝# 1 2 10 642
30ˡt̲x ← { d s c →
31 b ← f̲irst s
32 n ← 2 s̲elect s
33 k ← 3 s̲elect s
34 f ← 4 s̲elect s
35 (b = 0) ∧ c ≥ 0 ? 1 c̲at (d − 1) c̲at 10 c̲at 512 + 2 × c m̲od 256
36 b = 0 ? 0 0 0 0
37 n > 0 ? 1 c̲at (n − 1) c̲at k c̲at f
38 k = 1 ? 0 0 0 0
39 1 c̲at (d − 1) c̲at (k − 1) c̲at f d̲iv 2
40}
41
42⍝# l_ine s: the transmitter's line: 1 when idle, else the frame's bit 0.
43⍝# >> "ua:" u_se< "Uart"
44⍝# >> ua:l_ine 1 2 10 642
45⍝# 0
46ˡl̲ine ← { s → (1 − f̲irst s) + (f̲irst s) × (4 s̲elect s) m̲od 2 }
47
48⍝## The receiver
49
50⍝# Its state, six registers: phase (0 idle, 1 receiving, 2 waiting for
51⍝# the stop bit), clocks to the next sample, bits left, the bits so
52⍝# far, the last byte received, and valid (1 for the one clock after a
53⍝# byte arrives).
54⍝# >> "ua:" u_se< "Uart"
55⍝# >> ua:RxIdle
56⍝# 0 0 0 0 0 0
57ˡRxIdle ← 0 0 0 0 0 0
58
59⍝# d r_x s l: the receiver after one clock of the line l. A fall to 0
60⍝# while idle is a start bit; each data bit is sampled in its middle
61⍝# (d + d/2 clocks after the fall, then every d clocks), least
62⍝# significant first.
63⍝# >> "ua:" u_se< "Uart"
64⍝# >> u:r_x := ua:r_x 3
65⍝# >> ua:RxIdle u:r_x 0
66⍝# 1 3 8 0 0 0
67ˡr̲x ← { d s l →
68 p ← f̲irst s
69 n ← 2 s̲elect s
70 k ← 3 s̲elect s
71 sh ← 4 s̲elect s
72 da ← 5 s̲elect s
73 (p = 0) ∧ l = 0 ? 1 c̲at (d + (d d̲iv 2) − 1) c̲at 8 c̲at 0 c̲at da c̲at 0
74 p = 0 ? 0 0 0 0 c̲at da c̲at 0
75 p = 2 ? (2 × 1 − l) c̲at 0 0 0 c̲at da c̲at 0
76 n > 0 ? 1 c̲at (n − 1) c̲at k c̲at sh c̲at da c̲at 0
77 s2 ← (sh d̲iv 2) + 128 × l
78 k = 1 ? 2 0 0 0 c̲at s2 c̲at 1
79 1 c̲at (d − 1) c̲at (k − 1) c̲at s2 c̲at da c̲at 0
80}
81
82⍝# r_eceived s: the last byte received.
83⍝# >> "ua:" u_se< "Uart"
84⍝# >> ua:r_eceived 0 0 0 0 72 1
85⍝# 72
86ˡr̲eceived ← { s → 5 s̲elect s }
87
88⍝# v_alid? s: whether the last byte arrived on the last clock.
89⍝# >> "ua:" u_se< "Uart"
90⍝# >> ua:v_alid? 0 0 0 0 72 1
91⍝# 1
92ˡv̲alid? ← { s → 1 = 6 s̲elect s }
93
94⍝## Looped back
95
96⍝# The transmitter's line wired to the receiver, and a log of the first
97⍝# four bytes received with their count: fifteen registers, the
98⍝# transmitter's four, the receiver's six, the log's four, the count.
99⍝# >> "ua:" u_se< "Uart"
100⍝# >> ua:LoopIdle
101⍝# 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
102ˡLoopIdle ← 15 r̲eshape 0
103
104⍝# d l_oopback s c: one clock: the receiver samples the line as the
105⍝# transmitter drives it now, then both step, and a byte that arrives
106⍝# goes into the log at the count.
107⍝# >> "ua:" u_se< "Uart"
108⍝# >> "ck:" u_se< "Clocked"
109⍝# >> u:l_oop := ua:l_oopback 3
110⍝# >> ua:l_og ua:LoopIdle 'u:l_oop ck:r_un 3 ua:c_ommands 65
111⍝# 65
112ˡl̲oopback ← { d s c →
113 tx ← 4 t̲ake s
114 rx ← 6 t̲ake 4 d̲rop s
115 lg ← 4 t̲ake 10 d̲rop s
116 ct ← 15 s̲elect s
117 rx2 ← (d ˡr̲x rx)_ ˡl̲ine tx
118 v ← 6 s̲elect rx2
119 lg2 ← lg + v × ((o̲ffsets 4) = ct) × (5 s̲elect rx2) − lg
120 ((d ˡt̲x tx)_ c) c̲at rx2 c̲at lg2 c̲at ct + v
121}
122
123⍝# l_ines t: the transmitter's line for every row of a trace (of the
124⍝# transmitter, or of the looped-back pair: both start with its four
125⍝# registers).
126⍝# >> "ua:" u_se< "Uart"
127⍝# >> ua:l_ines 2 4 r_eshape 0 0 0 0 1 0 10 640
128⍝# 1 0
129ˡl̲ines ← { t →
130 b ← 1 s̲elect₂ t
131 (1 − b) + b × (4 s̲elect₂ t) m̲od 2
132}
133
134⍝# l_og s: the bytes received so far, from a looped-back state.
135⍝# >> "ua:" u_se< "Uart"
136⍝# >> ua:l_og 0 0 0 0 0 0 0 0 0 0 72 105 0 0 2
137⍝# 72 105
138ˡl̲og ← { s → (4 m̲in 15 s̲elect s) t̲ake 4 t̲ake 10 d̲rop s }
139
140⍝# The commands that send bytes (0 to 255), each when the transmitter
141⍝# is free: d c_ommands bytes gives one command per clock, the byte at
142⍝# the start of each frame of 10 d + 1 clocks, -1 elsewhere, with d
143⍝# more clocks at the end for the last stop bit.
144⍝# >> "ua:" u_se< "Uart"
145⍝# >> 2 ua:c_ommands 65
146⍝# 65 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
147ˡc̲ommands ← { d bytes →
148 per ← 1 + 10 × d
149 at ← per × o̲ffsets t̲ally bytes
150 all ← ((per × t̲ally bytes) + d) r̲eshape -1
151 all + ((o̲ffsets t̲ally all) '= t̲able at) '+ '× i̲nner bytes + 1
152}