Skip to content

Commit

Permalink
[minor] Add Instance Choice
Browse files Browse the repository at this point in the history
Add instance choice, a new feature that allows for one of several possible
instances to be instantiated at Verilog elaboration time.

Signed-off-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>
  • Loading branch information
nandor authored and seldridge committed Dec 9, 2024
1 parent 3ba7005 commit 595c3ed
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 0 deletions.
130 changes: 130 additions & 0 deletions abi.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,136 @@ bind Bar Bar_Layer1_Layer2 layer1_layer2(.notA(Bar.layer1.notA));
The `` `ifdef ``{.verilog} guards enable any combination of the bind files to be included while still producing legal SystemVerilog.
I.e., the end user may safely include none, either, or both of the bindings files.

## On Targets

A target will result in the creation of specialization files.
One specialization file per public module per option for a given target will be created.

Including a specialization file in the elaboration of Verilog produced by a FIRRTL compiler specializes the instantiation hierarchy under the associated public module with that option.

Each specialization file must have the following filename where `module` is the name of the public module, `target` is the name of the target, and `option` is the name of the option:

``` ebnf
filename = "targets_" , module , "_" , target , "_", option , ".vh" ;
```

Each specialization file will guard for multiple inclusion.
Namely, if two files that specialize the same target are included, this must produce an error.
If the file is included after a module which relies on the specialization, this must produce an error.

### Example

The following circuit is an example implementation of how a target can be lowered to align with the ABI defined above.
This circuit has one target named `Target` with two options, `A` and `B`.
Module `Foo` may be specialized using an instance choice that will instantiate `Bar` by default and `Baz` if `Target` is set to `A`.
If `Target` is set to `B`, then the default instantiation, `Bar`, will occur.
Module `Foo` includes a probe whose define is inside the instance choice.

``` firrtl
FIRRTL version 4.1.0
circuit Foo:
option Target:
case A
case B
module Baz:
output a: Probe<UInt<1>>
node c = UInt<1>(1)
define a = probe(c)
module Bar:
output a: Probe<UInt<1>>
node b = UInt<1>(0)
define a = probe(b)
public module Foo:
output a: Probe<UInt<1>>
instchoice x of Bar, Target:
A => Baz
define a = x.a
```

To align with the ABI, this must produce the following files to specialize the circuit for option `A` or option `B`, respectively:

- `targets_Foo_Target_A.sv`
- `targets_Foo_Target_B.sv`

What follows describes a possible implementation that aligns with the ABI.
Note that the internal details are not part of the ABI.

When compiled, this produces the following Verilog:

``` systemverilog
module Baz(output a);
assign a = 1'h1;
endmodule
module Bar(output a);
assign a = 1'h0;
endmodule
// Defines for the instance choices
`ifndef __target_Target_foo_x
`define __target_Target_foo_x Bar
`endif
module Foo();
`__target_Target_foo_x x();
endmodule
```

The contents of the two option enabling files are shown below:

``` systemverilog
// Contents of "targets_Target_A.vh"
`ifdef __target_Target_foo_x
`ERROR__target_Target_foo_x__must__not__be__set
`else
`define __target_Target_foo_x Baz
`endif
`ifdef __targetref_Foo_x_a a
`ERROR__targetref_Foo_x_a__must__not__be__set
`else
`define __targetref_Foo_x_a a
`endif
```

``` systemverilog
// Contents of "targets_Target_B.vh"
`ifdef __target_Target_foo_x
`ERROR__target_Target_foo_x__must__not__be__set
`endif
// This file has no defines.
```

Additionally, probe on public module `Foo` requires that the following file is produced:

``` systemverilog
// Contents of "refs_Foo.sv"
`ifndef __targetref_Foo_x_a
`define __targetref_Foo_x_a b
`endif
`define ref_Foo_x x.`__targetref_Foo_x_a
```

If neither of the option enabling files are included, then `Bar` will by default be instantiated.
If `targets_Foo_Target_A.vh` is included before elaboration of `Foo`, then `Baz` will be instantiated.
If `targets_Foo_Target_B.vh` is included before elaboration of `Foo`, then `Bar` will be instantiated.
If both `targets_Foo_Target_A.vh` and `targets_Foo_Target_B.vh` are included, then an error (by means of an undefined macro error) will be produced.
If either `targets_Foo_Target_A.vh` or `targets_Foo_Target_B.vh` are included after `Foo` is elaborated, then an error will be produced.

If `ref_Foo.vh` is included before either `targets_Foo_Target_A.vh` or `targets_Foo_Target_B.vh`, then an error will be produced.

## On Types

Types are only guaranteed to follow this lowering when the Verilog type is on an element which is part of the ABI defined public elements.
Expand Down
3 changes: 3 additions & 0 deletions include/firrtl.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<item>extmodule</item>
<item>layer</item>
<item>formal</item>
<item>option</item>
</list>
<list name="keywords">
<item>input</item>
Expand All @@ -28,6 +29,8 @@
<item>public</item>
<item>enablelayer</item>
<item>bound</item>
<item>instchoice</item>
<item>option</item>
</list>
<list name="types">
<item>UInt</item>
Expand Down
2 changes: 2 additions & 0 deletions revision-history.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ revisionHistory:
# additions to the specification should append entries here.
thisVersion:
spec:
- Add instance choice.
abi:
- Add instance choice.
# Information about the old versions. This should be static.
oldVersions:
- version: 4.0.0
Expand Down
87 changes: 87 additions & 0 deletions spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,39 @@ circuit Foo :
assume(clk, gt(a, UInt(2)), UInt<1>(h1), "input a > 2")
formal testFoo of FooTest :
## Targets
A `target`{.firrtl} describes one way that a FIRRTL circuit may be specialized for a certain use case.
Here, specialization means choosing a specific option for a target.
It is often desirable to have one FIRRTL circuit that has different logic when simulated, synthesized and mapped to a field-programmable gate array (FPGA), or synthesized to a given process technology and standard cell library.
While this per-target customizability can be expressed and specialized in a frontend language that produces FIRRTL, it is often desirable to expose the target specialization in the FIRRTL.
By delaying the specialization, the specialization can either be done by a FIRRTL compiler or exposed in the artifacts of a FIRRTL compiler for specialization by the consumer.
Practically, targets describe a limited form of parameterization and, if not specialized by a FIRRTL compiler, allow for FIRRTL compilers to generate parametric artifacts (e.g., parametric Verilog).
The `option`{.firrtl} keyword declares an option group, which contains `case`{.firrtl} declarations naming the settings allotted to that option.
The circuit can be specialized for a single case of a given option at any time.
Multiple option groups can be declared to capture orthogonal dimensions of configuration.
Specialization can occur either in the compiler or it can be materialized in the lowering.
For details, consult the FIRRTL ABI specification.
Specialization is not mandatory: options can be left unspecified, resorting to explicitly-defined default behaviour.
A target may be declared using the `target`{.firrtl} keyword.
The available options for which a target may take are listed using the `option`{.firrtl} keyword.
An example FIRRTL circuit showing two targets, `Platform` and `Performance`, and their allowable options is shown below:
``` firrtl
circuit:
target Platform:
option FPGA
option ASIC
target Performance:
option Slow
option Fast
```

# Circuit Components
Expand Down Expand Up @@ -518,6 +551,47 @@ circuit Foo:
;; snippetend
```

#### Instance Choice

An instance choice is a submodule instance where the choice of submodule is conditioned based on the value of a `target`{.firrtl}.
This enables per-instance specialization for different targets.
Additionally, this is a mechanism for module replacement.

An instance choice declaration specifies the instance name and names the option group based on which the choices are selected.
A default module must be provided.
The default module is instantiated by the instance choice when an option for its associated target is not specified.
Subsequently, modules can be specified for the known choices of the selected option group.
An instance choice does not need to specify modules for all cases.
The instantiated modules must be either modules or external modules.

An example of an instance choice is shown below.
This instance choice is conditioned on the `Platform` target.
By default, it will instantiate `DefaultClockGate` and when `Platform` is `FPGA` it will instantiate `FPGAClockGate`.

``` firrtl
circuit:
target Platform:
option FPGA
option ASIC
module DefaultClockGate:
input clock_in: Clock
output clock_out: Clock
input enable: UInt<1>
extmodule FPGAClockGate:
input clock_in: Clock
output clock_out: Clock
input enable: UInt<1>
module InstanceChoice:
instchoice clock_gate of DefaultClockGate, Platform:
FPGA => FPGAClockGate
```

The type of an instance choice is the same as an instantiation of the default module.
The ports of all module choices must be the same as the default module.

### Memories

Memories are stateful elements of a design.
Expand Down Expand Up @@ -4270,6 +4344,7 @@ decl =
| decl_extmodule
| decl_layer
| decl_formal
| decl_option
| decl_type_alias ;
decl_module =
Expand Down Expand Up @@ -4302,6 +4377,12 @@ decl_formal_param =
| "[" , [ decl_formal_param , { "," , decl_formal_param } ] , "]"
| "{" , [ id , "=" , decl_formal_param , { "," , id , "=" , decl_formal_param } ] , "}"
decl_option =
"option" , id , ":" , [info] , newline, indent ,
{ "case" , id , ":" , [ info ] , newline } ,
dedent ;
decl_type_alias = "type", id, "=", type ;
port = ( "input" | "output" ) , id , ":" , (type | type_property) , [ info ] ;
Expand All @@ -4323,11 +4404,17 @@ circuit_component =
| circuit_component_wire
| circuit_component_reg
| circuit_component_inst
| circuit_component_instchoice
| circuit_component_mem ;
circuit_component_node = "node" , id , "=" , expr , [ info ] ;
circuit_component_wire = "wire" , id , ":" , type , [ info ] ;
circuit_component_inst = "inst" , id , "of" , id , [ info ] ;
circuit_component_instchoice =
"instchoice" , id , "of" , id , "," , id , ":" , newline ,
indent ,
{ id , "=>" , id , newline } ,
dedent;
circuit_component_reg =
"reg" , id , ":" , type , "," , expr , [ info ]
Expand Down

0 comments on commit 595c3ed

Please sign in to comment.