To simplify the writing of backends for functional languages or similar targets, Yosys provides an alternative intermediate representation called FunctionalIR which maps more directly on those targets.
FunctionalIR represents the design as a function `(inputs, current_state) -> (outputs, next_state)`.
This function is broken down into a series of assignments to variables.
Every input, output and state has a name (equal to their name in RTLIL), a sort and a kind.
The kind field usually remains as the default value `$input`, `$output` or `$state`, however some RTLIL cells such as `$assert` or `$anyseq` generate auxiliary inputs/outputs/states that are given a different kind to distinguish them from ordinary RTLIL inputs/outputs/states.
- To access an individual input/output/state, use `ir.input(name, kind)`, `ir.output(name, kind)` or `ir.state(name, kind)`. `kind` defaults to the default kind.
- To iterate over all inputs/outputs/states of a certain kind, methods `ir.inputs`, `ir.outputs`, `ir.states` are provided. Their argument defaults to the default kinds mentioned.
- To iterate over inputs/outputs/states of any kind, use `ir.all_inputs`, `ir.all_outputs` and `ir.all_states`.
- Outputs have a node that indicate the value of the output, this can be retrieved via `output.value()`.
- States have a node that indicate the next value of the state, this can be retrieved via `state.next_value()`.
They also have an initial value that is accessed as either `state.initial_value_signal()` or `state.initial_value_memory()`, depending on their sort.
Each node has a "function", which defines its operation (for a complete list of functions and a specification of their operation, see `functional.h`).
Functions are represented as an enum `Functional::Fn` and the function field can be accessed as `node.fn()`.
Since the most common operation is a switch over the function that also accesses the arguments, the `Node` class provides a method `visit` that implements the visitor pattern.
For example, for an addition node `node` with arguments `n1` and `n2`, `node.visit(visitor)` would call `visitor.add(node, n1, n2)`.
Visitor methods should be marked as `override` to provide compiler errors if the arguments are wrong.
Utility classes
-----------------
`functional.h` also provides utility classes that are independent of the main FunctionalIR representation but are likely to be useful for backends.
`Functional::Writer` provides a simple formatting class that wraps a `std::ostream` and provides the following methods:
-`writer << value` wraps `os << value`.
-`writer.print(fmt, value0, value1, value2, ...)` replaces `{0}`, `{1}`, `{2}`, etc in the string `fmt` with `value0`, `value1`, `value2`, resp.
Each value is formatted using `os << value`.
It is also possible to write `{}` to refer to one past the last index, i.e. `{1} {} {} {7} {}` is equivalent to `{1} {2} {3} {7} {8}`.
-`writer.print_with(fn, fmt, value0, value1, value2, ...)` functions much the same as `print` but it uses `os << fn(value)` to print each value and falls back to `os << value` if `fn(value)` is not legal.
`Functional::Scope` keeps track of variable names in a target language.
It is used to translate between different sets of legal characters and to avoid accidentally re-defining identifiers.
Users should derive a class from `Scope` and supply the following:
-`Scope<Id>` takes a template argument that specifies a type that's used to uniquely distinguish variables.
Typically this would be `int` (if variables are used for `Functional::IR` nodes) or `IdString`.
- The derived class should provide a constructor that calls `reserve` for every reserved word in the target language.
- A method `bool is_legal_character(char c, int index)` has to be provided that returns `true` iff `c` is legal in an identifier at position `index`.
Given an instance `scope` of the derived class, the following methods are then available:
-`scope.reserve(std::string name)` marks the given name as being in-use
-`scope.unique_name(IdString suggestion)` generates a previously unused name and attempts to make it similar to `suggestion`.
-`scope(Id id, IdString suggestion)` functions similar to `unique_name`, except that multiple calls with the same `id` are guaranteed to retrieve the same name (independent of `suggestion`).
`sexpr.h` provides classes that represent and pretty-print s-expressions.
S-expressions can be constructed with `SExpr::list`, for example `SExpr expr = SExpr::list("add", "x", SExpr::list("mul", "y", "z"))` represents `(add x (mul y z))`
(by adding `using SExprUtil::list` to the top of the file, `list` can be used as shorthand for `SExpr::list`).
For prettyprinting, `SExprWriter` wraps an `std::ostream` and provides the following methods:
-`writer << sexpr` writes the provided expression to the output, breaking long lines and adding appropriate indentation.
-`writer.open(sexpr)` is similar to `writer << sexpr` but will omit the last closing parenthesis.
Further arguments can then be added separately with `<<` or `open`.
This allows for printing large s-expressions without needing the construct the whole expression in memory first.
-`writer.open(sexpr, false)` is similar to `writer.open(sexpr)` but further arguments will not be indented.
This is used to avoid unlimited indentation on structures with unlimited nesting.
-`writer.close(n = 1)` closes the last `n` open s-expressions.
-`writer.push()` and `writer.pop()` are used to automatically close s-expressions.
`writer.pop()` closes all s-expressions opened since the last call to `writer.push()`.
-`writer.comment(string)` writes a comment on a separate-line.
`writer.comment(string, true)` appends a comment to the last printed s-expression.