From 93d89a745116d1321cf8a017d5a71a6d0253fce6 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Fri, 26 Jan 2024 13:33:52 -0500 Subject: [PATCH 1/9] Tweaks --- docs/lang/static.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/lang/static.md b/docs/lang/static.md index 827d7e52aa..1744540a87 100644 --- a/docs/lang/static.md +++ b/docs/lang/static.md @@ -1,7 +1,7 @@ # Static Timing By default, Calyx programs use a *latency-insensitive* model of computation. -This means that the compiler does not track the number of cycles it takes to perform a computation or run a control operator +This means that the compiler does not track the number of cycles it takes to perform a computation or run a control operator. In general, latency-insensitivity makes it easier to compose programs together and gives the compiler freedom to schedule operators however it wants. However, the generated hardware to schedule the execution may not be efficient–especially if the program can take advantage of the *latency* information. @@ -24,7 +24,7 @@ seq { } ``` -A simple trick to achieve this is adding an empty group with `@static(n)` attribute on it: +A simple way to achieve this is adding an empty group with `@static(n)` attribute on it: ``` cell { r = @std_reg(0); @@ -39,4 +39,5 @@ seq { } ``` -The static compilation pass `tdst` will never attempt to use the `delay_9`'s `done` condition and since there are no assignments in the group, it'll not generate any additional hardware. +The static compilation pass `tdst` will never attempt to use the `delay_9`'s `done` condition. +Further, since there are no assignments in the group, it will not generate any additional hardware. From 4cf003572ee2e12bad89aa4ed4d9986cc0835e02 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Fri, 26 Jan 2024 15:39:03 -0500 Subject: [PATCH 2/9] Guarantees --- docs/lang/static.md | 80 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/docs/lang/static.md b/docs/lang/static.md index 1744540a87..1fe2e22904 100644 --- a/docs/lang/static.md +++ b/docs/lang/static.md @@ -1,16 +1,84 @@ # Static Timing -By default, Calyx programs use a *latency-insensitive* model of computation. -This means that the compiler does not track the number of cycles it takes to perform a computation or run a control operator. -In general, latency-insensitivity makes it easier to compose programs together and gives the compiler freedom to schedule operators however it wants. -However, the generated hardware to schedule the execution may not be efficient–especially if the program can take advantage of the *latency* information. +By default, Calyx programs use a *latency-insensitive*, or *dynamic*, model of computation. +This means that the compiler does not know, track, or guarantee the number of cycles it takes to perform a computation or run a control operator. +This is in contrast to a *latency-sensitive*, or *static*, model of computation, where the number of cycles a component needs is known to, and honored by, the compiler. -More crucially, however, it is impossible for *latency-insensitive* programs to interact with *latency-sensitive* hardware implemented in RTL. +In general, latency-insensitivity makes it easier to compose programs. +It grants the compiler freedom to schedule operators however it wants, as long as it meets the program's dataflow constraints. -Calyx uses the `@static` attribute to provide latency information to various constructs and provides strong guarantees about the generated programs. +However, there are two drawbacks to this approach. +First, the generated hardware may not be efficient: if the compiler does not know how long computations take, it must schedule them conservatively. +Second, it is impossible for *latency-insensitive* programs to interact with *latency-sensitive* hardware implemented in RTL; +this prevents the use of black-box hardware designs. + +To address these issues, Calyx provides a `static` keyword that modifies components and groups, along with static variants of other control operators. + +## Static support in the Calyx IL + +### Static components + +Say we have a multiplier component, `std_mult`, which multiplies the values `left` and `right` and puts the result in `out`. +Its latency is 3 cycles. +We can declare it as follows: +``` +static<3> primitive std_mult[W](go: 1, left: W, right: W) -> (out: W); +``` +Compare this to the divider component `std_div`, whose latency is unknown: +``` +primitive std_div[W](go: 1, left: W, right: W) -> (out: W, done: 1) +``` +The key differences are: +- The `static` keyword is used to declare the component as static and to specify its latency. +- The `done` port is not present in the static component. + +A client of the divider must pass two inputs, raise the `go` signal, and wait for the component itself to raise its `done` signal. +In contrast, a client of the multiplier must pass two inputs and raise the `go` signal, but it does not need to wait for the component to raise a `done` signal. +It can simply and safely assume that the result will be available after 3 cycles. + + +### Static groups and relative timing guards + +Much like components, groups can be declared as static. +Since groups are just unordered sets of assignments, it pays to have a little more control over the scheduling of the assignments within a group. +Consider this group, which performs `ans := 6 * 7`: +``` +static<4> group mult_and_store { + mult.left = %[0:3] ? 6; + mult.right = %[0:3] ? 7; + mult.go = %[0:3] ? 1; + ans.in = %3 ? mult.out; + ans.write_en = %3 ? 1; +} +``` +The `static<4>` keyword specifies that the group should take 4 cycles to execute. +However, we cannot just let all the assignments execute in any order, as we could with a dynamic group. + +The first three assignments are guarded (using the standard `?` operator) by the *relative timing guard* `%[0:3]`. +That is, they execute only in the first three cycles of the group's execution. +The guard `%3`, which we see thereafter, is syntactic sugar for `%[3:4]`. +In general, a guard `%[i:j]` is true in the half-open interval from cycle `i` to +cycle `j` of the group’s execution. + +### Static control operators + +Calyx provides static variants of each of its control operators. +While dynamic commands may contain both static and dynamic children, static commands must only have static children. + +- `static_seq` is a static version of `seq`; its latency is the sum of the latencies of its children. +- `static_par` is a static version of `par`; its latency is the maximum of the latencies of its children. +- `static_if` is a static version of `if`; its latency is the maximum of the latencies of its children. +- Calyx's `while` loop is unbouded, so it does not have a static variant. +- `static_repeat` is a static version of `repeat`; its latency is the product of the number of iterations and the latency of its child. +- `static_invoke` is a static version of `invoke`; its latency is the latency of the invoked cell. ## Guarantees +The `static` keyword is a promise to the compiler that the component or group will take exactly the specified number of cycles to execute. +The compiler is free to take advantage of this promise to generate more efficient hardware. +In return, the compiler must access out-ports of static components only after the specified number of cycles have passed, or risk receiving incorrect results. + + ## Tricks & Tips ### Delay by `n` cycles From a1df58fa7b3e8bc71fd1940e170c7129c0f50ddf Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Mon, 29 Jan 2024 17:41:43 -0500 Subject: [PATCH 3/9] nix tricks and tips --- docs/lang/static.md | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/docs/lang/static.md b/docs/lang/static.md index 1fe2e22904..a7fc789fb0 100644 --- a/docs/lang/static.md +++ b/docs/lang/static.md @@ -77,35 +77,3 @@ While dynamic commands may contain both static and dynamic children, static comm The `static` keyword is a promise to the compiler that the component or group will take exactly the specified number of cycles to execute. The compiler is free to take advantage of this promise to generate more efficient hardware. In return, the compiler must access out-ports of static components only after the specified number of cycles have passed, or risk receiving incorrect results. - - -## Tricks & Tips - -### Delay by `n` cycles - -Sometimes it can be useful to delay the execution of a group by `n` cycles: -``` -seq { - @static(1) a; // Run in first cycle - ??? - @static(2) b; // Run in the 10th cycle -} -``` - -A simple way to achieve this is adding an empty group with `@static(n)` attribute on it: -``` -cell { - r = @std_reg(0); -} -@static(9) group delay_9 { - delay_9[done] = r.out; // Don't use r.done here -} -seq { - @static(1) a; // Run in first cycle - @static(9) delay_9; - @static(2) b; // Run in the 10th cycle -} -``` - -The static compilation pass `tdst` will never attempt to use the `delay_9`'s `done` condition. -Further, since there are no assignments in the group, it will not generate any additional hardware. From 38e4f6032365703ae61674162bcf3a445709bf49 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Mon, 29 Jan 2024 17:42:50 -0500 Subject: [PATCH 4/9] Correct syntax --- docs/lang/static.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/lang/static.md b/docs/lang/static.md index a7fc789fb0..8763b752cc 100644 --- a/docs/lang/static.md +++ b/docs/lang/static.md @@ -65,12 +65,12 @@ cycle `j` of the group’s execution. Calyx provides static variants of each of its control operators. While dynamic commands may contain both static and dynamic children, static commands must only have static children. -- `static_seq` is a static version of `seq`; its latency is the sum of the latencies of its children. -- `static_par` is a static version of `par`; its latency is the maximum of the latencies of its children. -- `static_if` is a static version of `if`; its latency is the maximum of the latencies of its children. +- `static seq` is a static version of `seq`; its latency is the sum of the latencies of its children. +- `static par` is a static version of `par`; its latency is the maximum of the latencies of its children. +- `static if` is a static version of `if`; its latency is the maximum of the latencies of its children. - Calyx's `while` loop is unbouded, so it does not have a static variant. -- `static_repeat` is a static version of `repeat`; its latency is the product of the number of iterations and the latency of its child. -- `static_invoke` is a static version of `invoke`; its latency is the latency of the invoked cell. +- `static repeat` is a static version of `repeat`; its latency is the product of the number of iterations and the latency of its child. +- `static invoke` is a static version of `invoke`; its latency is the latency of the invoked cell. ## Guarantees From c710d289e5b218907b1f62212486137946f69396 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Mon, 29 Jan 2024 17:44:25 -0500 Subject: [PATCH 5/9] Title case --- docs/lang/static.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/lang/static.md b/docs/lang/static.md index 8763b752cc..2d3b4509c9 100644 --- a/docs/lang/static.md +++ b/docs/lang/static.md @@ -14,9 +14,9 @@ this prevents the use of black-box hardware designs. To address these issues, Calyx provides a `static` keyword that modifies components and groups, along with static variants of other control operators. -## Static support in the Calyx IL +## Static Support in the Calyx IL -### Static components +### Static Components Say we have a multiplier component, `std_mult`, which multiplies the values `left` and `right` and puts the result in `out`. Its latency is 3 cycles. @@ -37,7 +37,7 @@ In contrast, a client of the multiplier must pass two inputs and raise the `go` It can simply and safely assume that the result will be available after 3 cycles. -### Static groups and relative timing guards +### Static Groups and Relative Timing Guards Much like components, groups can be declared as static. Since groups are just unordered sets of assignments, it pays to have a little more control over the scheduling of the assignments within a group. @@ -60,7 +60,7 @@ The guard `%3`, which we see thereafter, is syntactic sugar for `%[3:4]`. In general, a guard `%[i:j]` is true in the half-open interval from cycle `i` to cycle `j` of the group’s execution. -### Static control operators +### Static Control Operators Calyx provides static variants of each of its control operators. While dynamic commands may contain both static and dynamic children, static commands must only have static children. From 7e91a2920a854a15668e77a08875a5d50829920c Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Mon, 29 Jan 2024 18:02:05 -0500 Subject: [PATCH 6/9] Various comments from Adrian --- docs/lang/static.md | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/docs/lang/static.md b/docs/lang/static.md index 2d3b4509c9..5064238c7d 100644 --- a/docs/lang/static.md +++ b/docs/lang/static.md @@ -6,15 +6,16 @@ This is in contrast to a *latency-sensitive*, or *static*, model of computation, In general, latency-insensitivity makes it easier to compose programs. It grants the compiler freedom to schedule operators however it wants, as long as it meets the program's dataflow constraints. +It also prevents code from implicitly depending on the state of other code running in parallel. However, there are two drawbacks to this approach. First, the generated hardware may not be efficient: if the compiler does not know how long computations take, it must schedule them conservatively. Second, it is impossible for *latency-insensitive* programs to interact with *latency-sensitive* hardware implemented in RTL; -this prevents the use of black-box hardware designs. +this means that the use of black-box hardware designs requires costly handshaking logic at the interface. -To address these issues, Calyx provides a `static` keyword that modifies components and groups, along with static variants of other control operators. +To address these issues, Calyx provides a `static` qualifier that modifies components and groups, along with static variants of other control operators. -## Static Support in the Calyx IL +## Static Constructs in the Calyx IL ### Static Components @@ -29,7 +30,7 @@ Compare this to the divider component `std_div`, whose latency is unknown: primitive std_div[W](go: 1, left: W, right: W) -> (out: W, done: 1) ``` The key differences are: -- The `static` keyword is used to declare the component as static and to specify its latency. +- The `static` qualifier is used to declare the component as static and to specify its latency. - The `done` port is not present in the static component. A client of the divider must pass two inputs, raise the `go` signal, and wait for the component itself to raise its `done` signal. @@ -41,6 +42,8 @@ It can simply and safely assume that the result will be available after 3 cycles Much like components, groups can be declared as static. Since groups are just unordered sets of assignments, it pays to have a little more control over the scheduling of the assignments within a group. +To this end, static groups have a unique feature that ordinary dynamic groups do not: *relative timing guards*. + Consider this group, which performs `ans := 6 * 7`: ``` static<4> group mult_and_store { @@ -52,21 +55,26 @@ static<4> group mult_and_store { } ``` The `static<4>` keyword specifies that the group should take 4 cycles to execute. -However, we cannot just let all the assignments execute in any order, as we could with a dynamic group. -The first three assignments are guarded (using the standard `?` operator) by the *relative timing guard* `%[0:3]`. -That is, they execute only in the first three cycles of the group's execution. +The first three assignments are guarded (using the standard `?` separator) by the relative timing guard `%[0:3]`. +In general, a relative timing guard `%[i:j]` is *true* in the half-open interval from cycle `i` to +cycle `j` of the group’s execution and *false* otherwise. + +In our case, the first three assignments execute only in the first three cycles of the group's execution. The guard `%3`, which we see thereafter, is syntactic sugar for `%[3:4]`. -In general, a guard `%[i:j]` is true in the half-open interval from cycle `i` to -cycle `j` of the group’s execution. +We have used it in this case to ensure that the last two assignments execute only in the last cycle of the group's execution. + ### Static Control Operators Calyx provides static variants of each of its control operators. While dynamic commands may contain both static and dynamic children, static commands must only have static children. -- `static seq` is a static version of `seq`; its latency is the sum of the latencies of its children. -- `static par` is a static version of `par`; its latency is the maximum of the latencies of its children. +- `static seq` is a static version of `seq`. +A child of `static seq` is guaranteed to begin executing exactly one cycle after the previous child has finished. +The latency of `static seq` is therefore the sum of the latencies of its children. +- `static par` is a static version of `par`. +All of its children begin executing at the same time, and its latency is therefore maximum of the latencies of its children. - `static if` is a static version of `if`; its latency is the maximum of the latencies of its children. - Calyx's `while` loop is unbouded, so it does not have a static variant. - `static repeat` is a static version of `repeat`; its latency is the product of the number of iterations and the latency of its child. From 29de48627b3d96461486dddbdc4b0de789c0c9f1 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Mon, 29 Jan 2024 18:17:22 -0500 Subject: [PATCH 7/9] Drag guarantees out into their own section --- docs/lang/static.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/lang/static.md b/docs/lang/static.md index 5064238c7d..051d77b73d 100644 --- a/docs/lang/static.md +++ b/docs/lang/static.md @@ -70,11 +70,8 @@ We have used it in this case to ensure that the last two assignments execute onl Calyx provides static variants of each of its control operators. While dynamic commands may contain both static and dynamic children, static commands must only have static children. -- `static seq` is a static version of `seq`. -A child of `static seq` is guaranteed to begin executing exactly one cycle after the previous child has finished. -The latency of `static seq` is therefore the sum of the latencies of its children. -- `static par` is a static version of `par`. -All of its children begin executing at the same time, and its latency is therefore maximum of the latencies of its children. +- `static seq` is a static version of `seq`; its latency is the sum of the latencies of its children. +- `static par` is a static version of `par`; its latency is the maximum of the latencies of its children. - `static if` is a static version of `if`; its latency is the maximum of the latencies of its children. - Calyx's `while` loop is unbouded, so it does not have a static variant. - `static repeat` is a static version of `repeat`; its latency is the product of the number of iterations and the latency of its child. @@ -85,3 +82,8 @@ All of its children begin executing at the same time, and its latency is therefo The `static` keyword is a promise to the compiler that the component or group will take exactly the specified number of cycles to execute. The compiler is free to take advantage of this promise to generate more efficient hardware. In return, the compiler must access out-ports of static components only after the specified number of cycles have passed, or risk receiving incorrect results. + +There are other guarantees associated with individual static constructs: +- A child of `static seq` is guaranteed to begin executing exactly one cycle after the previous child has finished. +- All the children of a `static par` are guaranteed to begin executing at the same time. +- The body of a `static repeat` is guaranteed to begin executing exactly one cycle after the previous iteration has finished. \ No newline at end of file From 49fc54c2bdd5ce3f03e821ff19972ddb4d37a172 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Tue, 30 Jan 2024 13:54:41 -0500 Subject: [PATCH 8/9] Push guarantees in throughout --- docs/lang/static.md | 68 ++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/docs/lang/static.md b/docs/lang/static.md index 051d77b73d..0965910101 100644 --- a/docs/lang/static.md +++ b/docs/lang/static.md @@ -5,7 +5,7 @@ This means that the compiler does not know, track, or guarantee the number of cy This is in contrast to a *latency-sensitive*, or *static*, model of computation, where the number of cycles a component needs is known to, and honored by, the compiler. In general, latency-insensitivity makes it easier to compose programs. -It grants the compiler freedom to schedule operators however it wants, as long as it meets the program's dataflow constraints. +It grants the compiler freedom to schedule operators however it wants, as long as the schedule meets the program's dataflow constraints. It also prevents code from implicitly depending on the state of other code running in parallel. However, there are two drawbacks to this approach. @@ -15,27 +15,38 @@ this means that the use of black-box hardware designs requires costly handshakin To address these issues, Calyx provides a `static` qualifier that modifies components and groups, along with static variants of other control operators. +Broadly, the `static` qualifier is a promise to the compiler that the component or group will take exactly the specified number of cycles to execute. +The compiler is free to take advantage of this promise to generate more efficient hardware. +In return, the compiler must access out-ports of static components only after the specified number of cycles have passed, or risk receiving incorrect results. + ## Static Constructs in the Calyx IL +We will now discuss the static constructs available in the Calyx IL, along with the guarantees they come with. + ### Static Components -Say we have a multiplier component, `std_mult`, which multiplies the values `left` and `right` and puts the result in `out`. -Its latency is 3 cycles. -We can declare it as follows: +Briefly consider a divider component, `std_div`, which divides the value `left` by the value `right` and puts the result in `out`. +This component is dynamic; its latency is unknown. ``` -static<3> primitive std_mult[W](go: 1, left: W, right: W) -> (out: W); +primitive std_div[W](go: 1, left: W, right: W) -> (out: W, done: 1); ``` -Compare this to the divider component `std_div`, whose latency is unknown: +A client of the divider must pass two inputs, raise the `go` signal, and wait for the component itself to raise its `done` signal. +The client can then read the result from the `out` port. + +Compare this to a multiplier component, `std_mult`, which has a similar signature but whose latency is known to be three cycles. +We declare it as follows: ``` -primitive std_div[W](go: 1, left: W, right: W) -> (out: W, done: 1) +static<3> primitive std_mult[W](go: 1, left: W, right: W) -> (out: W); ``` + The key differences are: - The `static` qualifier is used to declare the component as static and to specify its latency. - The `done` port is not present in the static component. -A client of the divider must pass two inputs, raise the `go` signal, and wait for the component itself to raise its `done` signal. -In contrast, a client of the multiplier must pass two inputs and raise the `go` signal, but it does not need to wait for the component to raise a `done` signal. +A client of the multiplier must pass two inputs and raise the `go` signal as before. +However, the client need not then wait for the component to indicate completion. It can simply and safely assume that the result will be available after 3 cycles. +This is a guarantee that the author of the component has made to the client, and the compiler is free to take advantage of it. ### Static Groups and Relative Timing Guards @@ -44,7 +55,8 @@ Much like components, groups can be declared as static. Since groups are just unordered sets of assignments, it pays to have a little more control over the scheduling of the assignments within a group. To this end, static groups have a unique feature that ordinary dynamic groups do not: *relative timing guards*. -Consider this group, which performs `ans := 6 * 7`: +Consider this group, which multiplies `6` and `7` and stores the result in `ans`. + ``` static<4> group mult_and_store { mult.left = %[0:3] ? 6; @@ -54,14 +66,14 @@ static<4> group mult_and_store { ans.write_en = %3 ? 1; } ``` -The `static<4>` keyword specifies that the group should take 4 cycles to execute. +The `static<4>` qualifier specifies that the group should take 4 cycles to execute. The first three assignments are guarded (using the standard `?` separator) by the relative timing guard `%[0:3]`. In general, a relative timing guard `%[i:j]` is *true* in the half-open interval from cycle `i` to cycle `j` of the group’s execution and *false* otherwise. In our case, the first three assignments execute only in the first three cycles of the group's execution. -The guard `%3`, which we see thereafter, is syntactic sugar for `%[3:4]`. +The guard `%3`, which we see immediately afterwards, is syntactic sugar for `%[3:4]`. We have used it in this case to ensure that the last two assignments execute only in the last cycle of the group's execution. @@ -69,21 +81,21 @@ We have used it in this case to ensure that the last two assignments execute onl Calyx provides static variants of each of its control operators. While dynamic commands may contain both static and dynamic children, static commands must only have static children. - -- `static seq` is a static version of `seq`; its latency is the sum of the latencies of its children. -- `static par` is a static version of `par`; its latency is the maximum of the latencies of its children. +In the examples below, assume that `A5`, `B6`, `C7`, and `D8` are static groups with latencies 5, 6, 7, and 8, respectively. + +- `static seq` is a static version of `seq`. +If we have `static seq { A5; B6; C7; D8; }`, we can guarantee that the latency of the entire operation is the sum of the latencies of its children: 5 + 6 + 7 + 8 = 26 cycles in this case. +We can also guarantee that, each child will begin executing exactly one cycle after the previous child has finished. +In our case, for example, `B6` will begin executing exactly one cycle after `A5` has finished. +- `static par` is a static version of `par`. +If we have `static par { A5; B6; C7; D8; }`, we can guarantee that the latency of the entire operation is the maximum of the latencies of its children: 8 cycles in this case. +Further, all the children of a `static par` are guaranteed to begin executing at the same time. +The children can rely on this "lockstep" behavior and can communicate with each other. +Such communication is undefined behavior in a dynamic `par`. - `static if` is a static version of `if`; its latency is the maximum of the latencies of its children. -- Calyx's `while` loop is unbouded, so it does not have a static variant. +For example, `static if { A5; B6; }` has a latency of 6 cycles. +- Calyx's `while` loop is unbouded and so it does not have a static variant. - `static repeat` is a static version of `repeat`; its latency is the product of the number of iterations and the latency of its child. -- `static invoke` is a static version of `invoke`; its latency is the latency of the invoked cell. - -## Guarantees - -The `static` keyword is a promise to the compiler that the component or group will take exactly the specified number of cycles to execute. -The compiler is free to take advantage of this promise to generate more efficient hardware. -In return, the compiler must access out-ports of static components only after the specified number of cycles have passed, or risk receiving incorrect results. - -There are other guarantees associated with individual static constructs: -- A child of `static seq` is guaranteed to begin executing exactly one cycle after the previous child has finished. -- All the children of a `static par` are guaranteed to begin executing at the same time. -- The body of a `static repeat` is guaranteed to begin executing exactly one cycle after the previous iteration has finished. \ No newline at end of file +For example, `static repeat 7 { B6; }` has a latency of 42 cycles. +The body of a `static repeat` is guaranteed to begin executing exactly one cycle after the previous iteration has finished. +- `static invoke` is a static version of `invoke`; its latency is the latency of the invoked cell. \ No newline at end of file From cfa47a9bfa78e361a35910fff80d0ddf1228dc28 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan Date: Thu, 1 Feb 2024 16:42:04 -0500 Subject: [PATCH 9/9] More headings for better linking --- docs/lang/static.md | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/docs/lang/static.md b/docs/lang/static.md index 0965910101..4e3dc30519 100644 --- a/docs/lang/static.md +++ b/docs/lang/static.md @@ -32,6 +32,7 @@ primitive std_div[W](go: 1, left: W, right: W) -> (out: W, done: 1); ``` A client of the divider must pass two inputs, raise the `go` signal, and wait for the component itself to raise its `done` signal. The client can then read the result from the `out` port. +That is, it obeys the [go-done interface][go-done-interface]. Compare this to a multiplier component, `std_mult`, which has a similar signature but whose latency is known to be three cycles. We declare it as follows: @@ -68,7 +69,7 @@ static<4> group mult_and_store { ``` The `static<4>` qualifier specifies that the group should take 4 cycles to execute. -The first three assignments are guarded (using the standard `?` separator) by the relative timing guard `%[0:3]`. +The first three assignments are guarded (using the [standard `?` separator][guard-sep]) by the relative timing guard `%[0:3]`. In general, a relative timing guard `%[i:j]` is *true* in the half-open interval from cycle `i` to cycle `j` of the group’s execution and *false* otherwise. @@ -79,23 +80,41 @@ We have used it in this case to ensure that the last two assignments execute onl ### Static Control Operators -Calyx provides static variants of each of its control operators. +Calyx provides static variants of each of its [control operators][]. While dynamic commands may contain both static and dynamic children, static commands must only have static children. In the examples below, assume that `A5`, `B6`, `C7`, and `D8` are static groups with latencies 5, 6, 7, and 8, respectively. -- `static seq` is a static version of `seq`. +#### `static seq`, a static version of [`seq`][seq] If we have `static seq { A5; B6; C7; D8; }`, we can guarantee that the latency of the entire operation is the sum of the latencies of its children: 5 + 6 + 7 + 8 = 26 cycles in this case. We can also guarantee that, each child will begin executing exactly one cycle after the previous child has finished. In our case, for example, `B6` will begin executing exactly one cycle after `A5` has finished. -- `static par` is a static version of `par`. + +#### `static par`, a static version of [`par`][par] If we have `static par { A5; B6; C7; D8; }`, we can guarantee that the latency of the entire operation is the maximum of the latencies of its children: 8 cycles in this case. Further, all the children of a `static par` are guaranteed to begin executing at the same time. The children can rely on this "lockstep" behavior and can communicate with each other. -Such communication is undefined behavior in a dynamic `par`. -- `static if` is a static version of `if`; its latency is the maximum of the latencies of its children. -For example, `static if { A5; B6; }` has a latency of 6 cycles. -- Calyx's `while` loop is unbouded and so it does not have a static variant. -- `static repeat` is a static version of `repeat`; its latency is the product of the number of iterations and the latency of its child. -For example, `static repeat 7 { B6; }` has a latency of 42 cycles. +Such communication is undefined behavior in a standard, dynamic, `par`. + +#### `static if`, a static version of [`if`][if] +If we have `static if { A5; B6; }`, we can guarantee that the latency of the entire operation is the maximum of the latencies of its children: 6 cycles in this case. + + +#### `static repeat`, a static version of [`repeat`][repeat] + +> Calyx's `while` loop is unbouded and so it does not have a static variant. + +If we have `static repeat 7 { B6; }`, we can guarantee that the latency of the entire operation is the product of the number of iterations and the latency of its child: 7 × 6 = 42 cycles in this case. The body of a `static repeat` is guaranteed to begin executing exactly one cycle after the previous iteration has finished. -- `static invoke` is a static version of `invoke`; its latency is the latency of the invoked cell. \ No newline at end of file + +#### `static invoke`, a static version of [`invoke`][invoke] + +Its latency is the latency of the invoked cell. + +[guard-sep]: ./ref.md#guarded-assignments +[go-done-interface]: ./ref.md#the-go-done-interface +[control operators]: ./ref.md#the-control-operators +[seq]: ./ref.md#seq +[par]: ./ref.md#par +[if]: ./ref.md#if +[repeat]: ./ref.md#repeat +[invoke]: ./ref.md#invoke