Skip to content

Commit e485dca

Browse files
Sean Purser-Haskellcopybara-github
authored andcommitted
Add design doc for xlscc's scoped conditional activation barriers.
PiperOrigin-RevId: 913908403
1 parent 13e43c1 commit e485dca

2 files changed

Lines changed: 294 additions & 0 deletions

File tree

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
# XLS[cc] Scoped Conditional Activation Barriers.
2+
3+
[TOC]
4+
5+
## Basic Principle
6+
7+
XLS[cc] has the concept of “activation barriers”, which solve mutual exclusion
8+
errors by putting IO ops into a new activation after the
9+
barrier. In order to avoid cycles, the condition of ops after the barrier must
10+
not depend directly on the data received from ops before it. This seems
11+
infeasible in the general case:
12+
13+
14+
```c++
15+
int x = in.read();
16+
if (x > 10) {
17+
__xlscc_activation_barrier</*conditional=*/true>();
18+
}
19+
// ...
20+
if (x == 20) {
21+
int y = in.read();
22+
// ...
23+
}
24+
```
25+
26+
## Solution: Scoped barriers
27+
28+
It can be made feasible for many useful cases by taking advantage of C++ scopes:
29+
30+
```c++
31+
int x = in.read();
32+
if (x > 10) {
33+
__xlscc_activation_barrier</*conditional=*/true>();
34+
int y = in.read();
35+
// ...
36+
}
37+
```
38+
39+
In this situation, we know that y = in.read() cannot be active in the same
40+
activation as x = in.read(), because the barrier appears before y = in.read() in
41+
the same scope.
42+
43+
However, “!(x > 10)” cannot be directly added to the condition for y =
44+
in.read(), because there would still be a data dependency.
45+
46+
47+
## Implementation of scoped mutual exclusion
48+
49+
There are two areas to address:
50+
1. How does the FSM determine which slices are active in a given activation
51+
without referencing any side-effecting values across the conditional barrier?
52+
- For unconditional barriers, this is trivial: nothing after the next
53+
barrier can be active, and this is known just from this activation’s
54+
starting slice index
55+
2. How does the FSM ensure that the function slices do not reference any
56+
side-effecting values across the conditional barrier?
57+
- For unconditional barriers, this is trivial: every continuation input can
58+
be reset to directly reference state after the barrier
59+
60+
### Slice activation calculation
61+
62+
Determining when the scoped slices are active is feasible based on the principle
63+
that slices in the conditional scope cannot be active in the same activation as
64+
the barrier (start) slice. Either the condition is:
65+
66+
- True: In which case they will be active in the next activation
67+
- False: In which they will never be active this Run() iteration
68+
69+
If the activation starts within the scoped slices, then they can be active.
70+
71+
Slices after the scope must be inactive if the barrier is active this iteration.
72+
This is achieved using the direct, side-effecting value referencing condition of
73+
the barrier. Each barrier resets this after-barrier condition to literal 0, so
74+
adding another barrier can remove this direct dependency.
75+
76+
This rule works with nested scopes because in order to reach a barrier in an
77+
inner scope, all the outer scopes’ barriers’ conditions must also have been
78+
true. Therefore, if the outer most scope’s condition was:
79+
80+
- True: Then it must be true that slices after the scope are after an active barrier
81+
- False: Then it must be false that slices after the scope are after an active barrier
82+
83+
### Example A, data_in receives values 1, 1, 4:
84+
```c++
85+
// Inactive this activation
86+
//** Inactive this activation via direct reference to received value(s)
87+
```
88+
89+
```c++
90+
const int a = data_in.read();
91+
if (a == 1) {
92+
__xlscc_activation_barrier</*conditional=*/true>();
93+
// const int b = data_in.read();
94+
// if (b == 1) {
95+
// __xlscc_activation_barrier</*conditional=*/true>();
96+
// const int c = data_in.read();
97+
// data_out.write(a + b + c);
98+
// }
99+
// ctrl_a.write(100);
100+
}
101+
ctrl_b.write(2);
102+
```
103+
104+
105+
```c++
106+
// const int a = data_in.read();
107+
// if (a == 1) {
108+
// __xlscc_activation_barrier</*conditional=*/true>();
109+
const int b = data_in.read();
110+
if (b == 1) {
111+
__xlscc_activation_barrier</*conditional=*/true>();
112+
// const int c = data_in.read();
113+
// data_out.write(a + b + c);
114+
// }
115+
//** ctrl_a.write(100);
116+
}
117+
//**ctrl_b.write(2);
118+
```
119+
120+
121+
```c++
122+
// const int a = data_in.read();
123+
// if (a == 1) {
124+
// __xlscc_activation_barrier</*conditional=*/true>();
125+
// const int b = data_in.read();
126+
// if (b == 1) {
127+
// __xlscc_activation_barrier</*conditional=*/true>();
128+
const int c = data_in.read();
129+
data_out.write(a + b + c);
130+
}
131+
ctrl_a.write(100);
132+
}
133+
ctrl_b.write(2);
134+
```
135+
136+
### Example A, data_in receives values 1, 4:
137+
```c++
138+
// Inactive this activation
139+
//** Inactive this activation via direct reference to received value(s)
140+
```
141+
Activation 0:
142+
143+
```c++
144+
const int a = data_in.read();
145+
if (a == 1) {
146+
__xlscc_activation_barrier</*conditional=*/true>();
147+
// const int b = data_in.read();
148+
// if (b == 1) {
149+
// __xlscc_activation_barrier</*conditional=*/true>();
150+
// const int c = data_in.read();
151+
// data_out.write(a + b + c);
152+
// }
153+
// ctrl_a.write(100);
154+
}
155+
//**ctrl_b.write(2);
156+
```
157+
158+
Activation 1:
159+
160+
161+
```c++
162+
// const int a = data_in.read();
163+
// if (a == 1) {
164+
// __xlscc_activation_barrier</*conditional=*/true>();
165+
const int b = data_in.read();
166+
if (b == 1) {
167+
__xlscc_activation_barrier</*conditional=*/true>();
168+
// const int c = data_in.read();
169+
// data_out.write(a + b + c);
170+
}
171+
ctrl_a.write(100);
172+
}
173+
ctrl_b.write(2);
174+
```
175+
### Example A, data_in receives value 4:
176+
Activation 0:
177+
178+
179+
```c++
180+
const int a = data_in.read();
181+
if (a == 1) {
182+
__xlscc_activation_barrier</*conditional=*/true>();
183+
// const int b = data_in.read();
184+
// if (b == 1) {
185+
// __xlscc_activation_barrier</*conditional=*/true>();
186+
// const int c = data_in.read();
187+
// data_out.write(a + b + c);
188+
// }
189+
// ctrl_a.write(100);
190+
}
191+
ctrl_b.write(2);
192+
```
193+
194+
### Example B, data_in receives values 11, 10, 1:
195+
```c++
196+
// Inactive this activation
197+
//** Inactive this activation via direct reference to received value(s)
198+
```
199+
200+
Activation 0:
201+
202+
```c++
203+
const int a = data_in.read();
204+
int b = 0;
205+
if (a > 10) {
206+
__xlscc_activation_barrier</*conditional=*/true>();
207+
// b = data_in.read();
208+
// }
209+
210+
//**__xlscc_activation_barrier</*conditional=*/true>();
211+
//**int c = data_in.read();
212+
//**data_out.write(a + b + c);
213+
```
214+
215+
Activation 1:
216+
217+
```c++
218+
// const int a = data_in.read();
219+
// int b = 0;
220+
// if (a > 10) {
221+
// __xlscc_activation_barrier</*conditional=*/true>();
222+
b = data_in.read();
223+
}
224+
225+
__xlscc_activation_barrier</*conditional=*/false>(); // conditional=true is equivalent
226+
// int c = data_in.read();
227+
// data_out.write(a + b + c);
228+
```
229+
230+
Activation 2:
231+
232+
```c++
233+
// const int a = data_in.read();
234+
// int b = 0;
235+
// if (a > 10) {
236+
// __xlscc_activation_barrier</*conditional=*/true>();
237+
// b = data_in.read();
238+
// }
239+
240+
// __xlscc_activation_barrier</*conditional=*/false>(); // conditional=true is equivalent
241+
int c = data_in.read();
242+
data_out.write(a + b + c);
243+
```
244+
245+
246+
## Continuation value handling
247+
248+
Another source of cycles can be direct references from after the barrier to
249+
side-effecting values from before it. Section 1 only handles the implicit slice
250+
activity condition. Although operations before and after the barrier are
251+
mutually exclusive, according to the slice activity, XLS will still see a cycle
252+
and be unable to merge the operations.
253+
254+
For example:
255+
256+
```c++
257+
const int a = data_in.read();
258+
if (a <= 5) {
259+
__xlscc_activation_barrier</*conditional=*/true>();
260+
int b = 0;
261+
// Introduces data dependency
262+
if (a == 1) {
263+
b = data_in.read();
264+
}
265+
data_out.write(a + b);
266+
}
267+
```
268+
269+
With unconditional barriers, it is safe to simply remap every continuation value
270+
input* after the barrier to reference the continuation value’s state element:
271+
that is, to reference the value from the last activation. This is safe because
272+
an unconditional barrier will always proceed to the next activation, and so
273+
nothing after it can be active to use the value before it is loaded into the
274+
state element. For conditional barriers, it is possible that the condition will
275+
be false, in which case the FSM will fall through the barrier within the same
276+
activation, and the state elements will not have been updated. The solution to
277+
this is, again, scoping, this time of continuation input references.
278+
279+
Each conditional barrier’s scope maintains its own map of continuation value
280+
input references, much like the variable maps in the TranslationContext. When a
281+
new scope is started, the parent’s map is copied, and all applicable
282+
continuation inputs are reset to use state elements. When the scope ends,
283+
propagation down to the parent scope is done, so that direct references are
284+
referenced for any continuation values produced by slices inside the scope. This
285+
is because it is possible to fall out of the scope within a single activation
286+
(see section 1).
287+
288+
When the conditional barrier’s condition is: True: Continuation values produced
289+
by the scoped slices are fully valid, being computed from state elements that
290+
were updated during the activation transition. False: Continuation values
291+
produced by the scoped slices are invalid, referencing state values from a
292+
previous activation. However, they should never be used, since the select phis
293+
generated in the slice after the end of the scope will always ignore them.

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ nav:
8282
- Optimizations:
8383
- Value Set Simplification:
8484
- Division: 'design_docs/optimizations/value_set_simp/division_simplification_design.md'
85+
- XLS[cc] Activation Barriers: 'design_docs/xlscc_activation_barriers.md'
8586
- Releasing: 'releasing.md'
8687
- NoC:
8788
- Overview: 'noc/xls_noc_readme.md'

0 commit comments

Comments
 (0)