Skip to content

Commit

Permalink
[fud2] Document Syntax for Specifying Scripts (#2236)
Browse files Browse the repository at this point in the history
  • Loading branch information
jku20 committed Aug 13, 2024
1 parent dccf92b commit c41fdae
Showing 1 changed file with 93 additions and 1 deletion.
94 changes: 93 additions & 1 deletion docs/running-calyx/fud2/scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ plugins = ["/my/fancy/plugin.rhai"]
base = "..."
```

## Example Script
## Example Script in High Level Rhai

We'll walk through how to write a script that adds support for using the `calyx` compiler.

Expand All @@ -28,6 +28,63 @@ export const verilog_state = state("verilog", ["sv", "v"]);

These two lines define a `calyx` state and a `verilog` state. The `export` prefix means that these variables will be accessible to other scripts that `import "calyx"`.

Now we will define an operation taking the `calyx` state to the `verilog` state. These operations define functions whose arguments are input files and return values are output values. Their bodies consist of commands that will transform those inputs to those outputs.

```rust,ignore
// define an op called "calyx_to_verilog" taking a "calyx_state" to a "verilog_state".
defop calyx_to_verilog(c: calyx_state) >> v: verilog_state {
// retrieve a variable from the fud2.toml config
let calyx_base = config("calyx.base");
// retrieve a variable from the config, or a default value
let calyx_exe = config_or("calyx.exe", "${calyx_base}/target/debug/calyx");
let args = config_or("calyx.args", "");
// specify a shell command to turn a calyx file "c" into a verilog file "v"
shell("${calyx_exe} -l ${calyx_base} -b verilog ${args} ${c} > ${v});
}
```

Counterintuitively, `c`, `v`, `calyx_base`, `calyx_exe`, and `args` do not contain the actual variable values. They contain identifiers which are replaced by the values at runtime. For example, `print(args)` would print a `$args` instead of the value assigned by the config. An op cannot take different code paths based on config values or different input/output file names.

This example shows off nearly all of the features available for defining ops. Scripts can reuse functionality by exploiting the tools of Rhai scripting. For example, if we wanted to create a second, similar op `calyx_noverify`, we could factor the contents of `calyx_to_verilog` into a new function and call that function in both ops.

```
// a function constructing a shell command to take a calyx in_file to a verilog out_file
// this function adds `add_args` as extra arguments to it's call to the calyx compiler
fn calyx_cmd(in_file, out_file, add_args) {
let calyx_base = config("calyx.base");
let calyx_exe = config_or("calyx.exe", "${calyx_base}/target/debug/calyx");
let args = config_or("calyx.args", "");
shell("${calyx_exe} -l ${calyx_base} -b verilog ${args} ${add_args} ${in_file} > ${out_file});
}
// define an op called "calyx_to_verilog" taking a "calyx_state" to a "verilog_state".
defop calyx_to_verilog(c: calyx_state) >> v: verilog_state {
calyx_cmd(c, v, "");
}
// define an op called "calyx_noverify" taking a "calyx_state" to a "verilog_state".
defop calyx_to_verilog(c: calyx_state) >> v: verilog_state {
calyx_cmd(c, v, "--disable-verify");
}
```

## Example Script in Low Level Rhai

`fud2` generates Ninja build files. Low level Rhai gives more control over what generated build files look like.

We'll walk through how to write a script that adds support for using the `calyx` compiler.

Like before, we need to define some states:

```rust,ignore
export const calyx_state = state("calyx", ["futil"]);
export const verilog_state = state("verilog", ["sv", "v"]);
```

These two lines define a `calyx` state and a `verilog` state. The `export` prefix means that these variables will be accessible to other scripts that `import "calyx"`.

Next we'll define a setup procedure to define some rules that will be useful.

```rust,ignore
Expand Down Expand Up @@ -110,6 +167,41 @@ The name for an import is always just the basename of the script file, without a

## API

### High Level Rhai

#### defop

```
defop <op name>(<input1>: <input1 state>, <input2>: <input2 state> ...) >> <target1>: <target1 state>, <target2>, <target2 state> ... {
<statements>
}
```
Defines an op with `<op name>` which runs `<statements>` to generate target states from input states.

The name, inputs, and targets are called the signature of the op. `<statements>` is called the body of the op.

#### shell

```
shell(<string>)
```
When called in the body of an op, that op will run `<string>` as a shell command to generate its targets. It is an error to call `shell` outside of the body of an op. Additionally, it is an error to call `shell` in the body of an op in which `shell_deps` was called prior.

In the generated Ninja code, `shell` will create both a `rule` wrapping the shell command and a `build` command that invokes that rule. When a `defop` contains multiple `shell` commands, `fud2` automatically generates Ninja dependencies among the `build` command to ensure they run in order.

#### shell_deps

```
shell_deps(<string>, [<dep1>, <dep2>, ...], [<target1>, <target2>, ..])
```
When called in the body of an op, that op will run `<string>` as a shell command if it needs to generate `<target1>, <target2>, ...` from `<dep1>, <dep2>, ...`. It is an error to call `shell_deps` outside of the body of an op. Additionally, it is an error to call `shell_deps` in the body of an op in which `shell` was called prior.

Targets and deps are either strings, such as `"file1"`, or identifiers, such as if `c: calyx` existed in the signature of an op then `c` could be used as a target or dep.

A call to `shell_deps` corresponds directly to a Ninja rule in the Ninja file generated by `fud2`.

### Low Level Rhai

Currently, the Rhai API is almost identical to the Rust API. However `Emitter::add_file` is not currently supported. And `Emitter::var` is renamed to `_var` because `var` is a reserved keyword in Rhai.

### Adding to the API
Expand Down

0 comments on commit c41fdae

Please sign in to comment.