sourcelibs/Datapath/src/Datapath.xtl
1⍝# Datapath: the vector examples at a stated width: the whole-array operations a circuit realizes directly
2⍝# Import it with an alias of your choice: "dp:" u_se< "Datapath".
3⍝# Put libs/Datapath/src on XETAL_PATH ("just path"); the reference is libs/Datapath/docs.
4⍝# Names with l: are exported; the others are private to this file.
5⍝#
6⍝# Each function here is one whole-array expression a circuit can take
7⍝# on directly: arithmetic and comparisons item by item with scalar
8⍝# extension (one unit per element, or one unit and a counter), a
9⍝# select by mask (a mux), and reductions (a tree of adders). The
10⍝# arithmetic is Fixed's (a wrap at every unit), so every result is what w-bit units give, and
11⍝# the X_eTaL evaluator is the reference the generated circuit must
12⍝# match bit for bit (the demos are the programs it checks). The width
13⍝# comes first; a function of three or four arguments is used curried,
14⍝# the width bound once: u:v_add := dp:v_add 16, then x u:v_add y.
15ᶠˣ⁼u̲se< "Fixed"
16
17⍝## Elementwise: one unit per element
18
19⍝# w v_add x y: the sum, item by item, wrapped to w bits (eight 16-bit
20⍝# numbers: eight 16-bit adders).
21⍝# >> "dp:" u_se< "Datapath"
22⍝# >> u:v_add := dp:v_add 16
23⍝# >> 30000 u:v_add 30000 -30000
24⍝# -5536 0
25ˡv̲add ← { w x y → w ᶠˣw̲rap x + y }
26
27⍝# w s_axpy a x y: a times x plus y, wrapped at each unit (a multiplier
28⍝# and an adder per element); bind w and a: u:s_axpy := 16 dp:s_axpy 3.
29⍝# >> "dp:" u_se< "Datapath"
30⍝# >> u:s_axpy := 16 dp:s_axpy 3
31⍝# >> 1 2 3 4 u:s_axpy 100 200 300 400
32⍝# 103 206 309 412
33ˡs̲axpy ← { w a x y → w ᶠˣw̲rap y + w ᶠˣw̲rap a × x }
34
35⍝# r_elu x: x where positive, 0 elsewhere (a comparator and a mux).
36⍝# >> "dp:" u_se< "Datapath"
37⍝# >> dp:r_elu 1 -2 3 -4
38⍝# 1 0 3 0
39ˡr̲elu ← { x → x m̲ax 0 }
40
41⍝## Masks and select: a mux
42
43⍝# t t_hreshold x: x where x > t, 0 elsewhere: the mask x > t is the
44⍝# mux's select line.
45⍝# >> "dp:" u_se< "Datapath"
46⍝# >> 2 dp:t_hreshold 1 2 3 4
47⍝# 0 0 3 4
48ˡt̲hreshold ← { t x → x × x > t }
49
50⍝# m k_eep x: x where the mask m is 1, 0 elsewhere.
51⍝# >> "dp:" u_se< "Datapath"
52⍝# >> (1 0 1 0) dp:k_eep 5 6 7 8
53⍝# 5 0 7 0
54ˡk̲eep ← { m x → x × m }
55
56⍝## Reductions: a tree of adders
57
58⍝# w s_um x: the sum of a vector as a balanced tree of w-bit adders:
59⍝# the first half added to the second half, item by item, then that
60⍝# vector likewise, down to one number (a loop over the log2 n levels
61⍝# of the tree; an odd length is padded with a 0). Wrapping at every
62⍝# adder gives the same bits as wrapping the exact sum once (checked in
63⍝# tests/checks.xtl).
64⍝# >> "dp:" u_se< "Datapath"
65⍝# >> 16 dp:s_um 20000 20000 20000 20000
66⍝# 14464
67ˡs̲um ← { w x →
68 levels ← '+ r̲/ (2 ^ o̲ffsets 63) < t̲ally x
69 f̲irst levels '{ v → w ʰh̲alve v } p̲ower x
70}
71
72⍝# One level of the tree: the first half added to the second (after a
73⍝# 0 pads an odd length).
74ʰh̲alve ← { w v →
75 e ← v c̲at ((t̲ally v) m̲od 2) r̲eshape 0
76 h ← (t̲ally e) d̲iv 2
77 w ᶠˣw̲rap (h t̲ake e) + h d̲rop e
78}
79
80⍝# l_argest x: the largest item (a tree of comparators and muxes).
81⍝# >> "dp:" u_se< "Datapath"
82⍝# >> dp:l_argest 3 9 2
83⍝# 9
84ˡl̲argest ← { x → 'm̲ax r̲/ x }
85
86⍝# w d_ot x y: the inner product, a multiplier per element then the
87⍝# tree.
88⍝# >> "dp:" u_se< "Datapath"
89⍝# >> u:d_ot := dp:d_ot 16
90⍝# >> 1 2 3 u:d_ot 4 5 6
91⍝# 32
92ˡd̲ot ← { w x y → w ˡs̲um w ᶠˣw̲rap x × y }
93
94⍝## The acceptance pipeline
95
96⍝# w p_ipeline x k: multiply, add, select, reduce in one expression,
97⍝# the sum of the positive items of x + x * k, every unit w bits: the
98⍝# first end-to-end test research7 asks for (arithmetic, map, zip,
99⍝# comparison, selection, reduction).
100⍝# >> "dp:" u_se< "Datapath"
101⍝# >> u:p_ipeline := dp:p_ipeline 16
102⍝# >> 1 2 3 4 u:p_ipeline 1 -2 1 -2
103⍝# 8
104ˡp̲ipeline ← { w x k → w ˡs̲um ˡr̲elu w ᶠˣw̲rap x + w ᶠˣw̲rap x × k }