Skip to content

Commit 07170ce

Browse files
Sean Purser-Haskellcopybara-github
authored andcommitted
Add design doc for xlscc's scoped conditional activation barriers.
PiperOrigin-RevId: 914461247
1 parent 46400e4 commit 07170ce

2 files changed

Lines changed: 299 additions & 0 deletions

File tree

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

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)