From c7292b389a68770a5764e79c1a9909c0bfde23a8 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Thu, 21 Sep 2023 18:02:16 -0400 Subject: [PATCH] [major] Add public modules Add a "public" modules to FIRRTL. A public module is a module that is a root of the circuit. The notion of a circuit is then expanded to allow for multiple roots and these roots may have no common children. In effect, a circuit is a collection of multi-rooted instantiation trees. Modify the ABI to require that all public modules produce a filelist whose name is derived from the public module's name. Remove circuit name entirely as this has no bearing on the ABI anymore. As a result, Annotations can no longer target the circuit. Annotations which did this previously now must be "no target annotations". This was always supported and was how Chisel has always emitted them. Signed-off-by: Schuyler Eldridge --- abi.md | 115 ++++++++++++++++++-------- include/firrtl.xml | 1 + revision-history.yaml | 5 ++ spec.md | 185 +++++++++++++++++++++++++++++------------- 4 files changed, 216 insertions(+), 90 deletions(-) diff --git a/abi.md b/abi.md index e646f6f5..b25e0232 100644 --- a/abi.md +++ b/abi.md @@ -36,30 +36,68 @@ may evolve as new FIRRTL constructs are added. ## On Modules -### The Circuit and Top Level +### The Circuit -The top level module, as specified by the circuit name, shall be present as a -System Verilog module of the same name. This FIRRTL module is considered a -"public" module and subject to the lowering constraints for public modules. +The circuit is a container of public modules. The circuit, by itself, does not +have any ABI. ### External Modules An external module may be presumed to exist following the lowering constraints -for public modules. The module shall exist with a verilog name matching the -defname value or, lacking that, the module name. +for public modules. The module shall exist with a Verilog name matching the +defname value or, lacking that, the FIRRTL module name. The ports of an +external module shall adhere to one of the port lowering ABIs. -### Public Modules +No assumption is made of the filename of an implementation of an external +module. It is the user's job to include files in such a way in their tools to +resolve the name. -Any module considered a "public" module shall be implemented in Verilog in a -consistent way. Any public module shall exist as a Verilog module of the same -name. +### Public Modules -Each public module with definitions (e.g. not external modules) shall be placed -in a file with the same name. No assumption is made of the filename of an -implementation of an external module, it is the user's job to include files in -such a way in their tools to resolve the name. +All public modules shall be implemented in Verilog in a consistent way. All +public modules shall exist as Verilog modules of the same name. Each public +module shall be placed in a file with a format as follows where `module` is the +name of the public module: -### Port Lowering ABIv1 +``` ebnf +filename = module , ".sv" ; +``` + +Each public module in a circuit shall produce a filelist that contains the +filename of the file containing the public module and any necessary files that +define all public or private module files instantiated under it. Files that +define external modules are not included. This filelist shall have a name as +follows where `module` is the name of the public module: + +``` ebnf +filelist_filename = "filelist_" , module , ".f" ; +``` + +The filelist contents are a newline-delimited list of filenames. + +The ports of public modules shall be lowered using one of the Port Lowering ABIs. + +No information about the instantiations of a public module may be used to +compile a public module---a public module is compiled as if it is never +instantiated. However, it is legal to compile modules which instantiate public +modules with full knowledge of the public module internals. + +### Private Modules + +Private modules have no defined ABI. If the compilation of private modules +produces files, then those files shall be included when necessary in public +module filelists. + +FIRRTL compilers shall mangle the names of private modules that are not removed +by compilation. The mangling scheme is implementation defined. This is done to +avoid name collisions with the private modules produced by other compilations. + +### Port Lowering ABIs + +There are two supported port lowering ABIs. These ABIs are applicable to public +modules or external modules only. + +#### Port Lowering ABIv1 Ports are generally lowered to netlist types, except where Verilog's type system prevents it. @@ -68,8 +106,8 @@ Ports of integer types shall be lowered to netlist ports (`wire`{.verilog}) as a packed vector of equivalent size. For example, consider the following FIRRTL: ```FIRRTL -circuit Top : - module Top : +circuit : + public module Top : output out: UInt<16> input b: UInt<32> ``` @@ -86,16 +124,16 @@ module Top( Ports of aggregate type shall be scalarized according to the "Aggregate Type Lowering" description in the FIRRTL spec. -Ports of ref type shall be lowered to a Verilog macro of the form `` `define -ref___ `` in a -file with name `ref__.sv`. References to aggregates -will be lowered to a series of references to ground types. This ABI does not -specify whether the original aggregate referent is scalarized or not. +Ports of ref type on public modules shall, for each public module, be lowered to +a Verilog macro of the form `` `define ref__ `` in a file with name `ref_.sv`. References to +aggregates will be lowered to a series of references to ground types. This ABI +does not specify whether the original aggregate referent is scalarized or not. All other port types shall lower according ot the type lowering in section ["On Types"](#On-Types). -### Port Lowering ABIv2 +#### Port Lowering ABIv2 Ports are lowered per the v1 ABI above, except for aggregate types. @@ -137,30 +175,37 @@ used by a nested group will create output ports because SystemVerilog disallows the use of a bind instantiation underneath the scope of another bind instantiation. The names of additional ports are implementation defined. -For each optional group, one binding file will be produced for each group or -nested group. The circuit, group name, and any nested groups are used to derive -a predictable filename of the binding file. The file format uses the format -below: +For each optional group and public module, one binding file will be produced for +each group or nested group. The public module, group name, and any nested +groups are used to derive a predictable filename of the binding file. The file +format uses the format below where `module` is the name of the public module, +`root` is the name of the root-level group and `nested` is the name of zero or +more nested groups: ``` ebnf -filename = "groups_" , circuit , "_", root , { "_" , nested } , ".sv" ; +filename = "groups_" , module , "_", root , { "_" , nested } , ".sv" ; ``` As an example, consider the following circuit with three optional groups: ``` firrtl -circuit Foo: +circuit: declgroup Group1, bind: declgroup Group2, bind: declgroup Group3, bind: + public module Bar: + public module Baz: ``` -When compiled to Verilog, this will produce three bind files: +When compiled to Verilog, this will produce six bind files: ``` -groups_Foo_Group1.sv -groups_Foo_Group1_Group2.sv -groups_Foo_Group1_Group2_Group3_.sv +groups_Bar_Group1.sv +groups_Bar_Group1_Group2.sv +groups_Bar_Group1_Group2_Group3_.sv +groups_Baz_Group1.sv +groups_Baz_Group1_Group2.sv +groups_Baz_Group1_Group2_Group3_.sv ``` The contents of each binding files must have the effect of including all code @@ -178,7 +223,7 @@ module `Bar`. Both `Foo` and `Bar` contain groups. To make the example simpler, no constant propagation is done: ``` firrtl -circuit Foo: +circuit: declgroup Group1, bind: declgroup Group2, bind: @@ -197,7 +242,7 @@ circuit Foo: node notNotA = not(notA) define _notNotA = probe(notNotA) - module Foo: + public module Foo: inst bar of Bar group Group1: diff --git a/include/firrtl.xml b/include/firrtl.xml index da1ba258..b67b3eb3 100644 --- a/include/firrtl.xml +++ b/include/firrtl.xml @@ -25,6 +25,7 @@ invalidate intrinsic propassign + public UInt diff --git a/revision-history.yaml b/revision-history.yaml index fc7b171a..2280807f 100644 --- a/revision-history.yaml +++ b/revision-history.yaml @@ -5,7 +5,12 @@ revisionHistory: # additions to the specification should append entries here. thisVersion: spec: + - Add public modules. Change circuits to support any number of public + modules which may have disjoint instance hierarchies. abi: + - Add ABI for public modules and filelist output. + - Changed ABI for group and ref generated files. These now use the public + module and not the circuit. # Information about the old versions. This should be static. oldVersions: - version: 3.2.0 diff --git a/spec.md b/spec.md index 62fd4fc7..56bafc58 100644 --- a/spec.md +++ b/spec.md @@ -137,25 +137,61 @@ versioned release of this standard to include this preamble. ``` firrtl FIRRTL version 1.1.0 -circuit MyTop... +circuit : ``` # Circuits and Modules ## Circuits -All FIRRTL circuits consist of a list of modules, each representing a hardware -block that can be instantiated. The circuit must specify the name of the -top-level module. +A FIRRTL circuit is a collection of FIRRTL modules. Each module is a hardware +"unit" that has ports, registers, wires, and may instantiate other modules (see: +[@sec:modules]). (This is the same concept as a Verilog `module`{.verilog}.) A +public module may be instantiated outside the current circuit. Public modules +are the exported identifiers of a circuit. Any non-public module may not be +instantiated outside the current circuit. + +Consider the following circuit. This contains two modules, `Bar` and `Baz`. +Module `Baz` is marked public. ``` firrtl -circuit MyTop : - module MyTop : - ; ... - module MyModule : - ; ... +circuit : + module Bar : + input a: UInt<1> + output b: UInt<1> + + connect b, a + + public module Baz : + input a: UInt<1> + output b: UInt<1> + + inst bar of Bar + connect bar.a, a + connect b, bar.b +``` + +The circuit below is more complex. This circuit contains three public modules, +`Foo`, `Bar`, and `Baz`, and one non-public module, `Qux`. Module `Foo` has no +common instances with `Bar` or `Baz`. `Bar` and `Baz` both instantiate `Qux`: + +``` firrtl +circuit : + public module Foo : + + public module Bar : + inst qux of Qux + + public module Baz : + inst qux of Qux + + module Qux : ``` +A circuit that contains no public modules is trivially equivalent to a circuit +that contains no modules. It is not enforced that a circuit has at least one +public module, but it is expected that it does. + ## Modules Each module has a given name, a list of ports, and a list of statements @@ -179,6 +215,38 @@ physically present in the final circuit. Refer to the description of the instance statement for details on how to instantiate a module ([@sec:instances]). +### Public Modules + +A public module is a module that is marked with the `public`{.firrtl} keyword. +A public module may be instantiated outside by other circuits. + +The example below shows a public module `Foo`: + +``` firrtl +public module Foo: + input a: UInt<1> + output b: UInt<1> + + connect b, a +``` + +A public module has a number of restrictions: + +1. A public module may have no ports of uninferred width. +1. A public module may have no ports of abstract reset type. +1. A public module may have no ports of input probe type. +1. A `RWProbe`{.firrtl} may not be used to access a public module's ports. +1. A public module may be instantiated by other modules within a circuit, but + the behavior of the module must not be affected by these instantiations. + +For more information on the lowering of public modules, see the FIRRTL ABI +Specification. + +### Private Modules + +A private module is any module which is not public. Private modules have none +of the restrictions of public modules. + ## Optional Groups Optional groups are named collections of statements inside a module. Optional @@ -219,7 +287,7 @@ contains a group definition that creates a node computed from a port defined in the scope of `Foo`. ``` firrtl -circuit Foo: +circuit: declgroup Bar, bind: ; Declaration of group Bar with convention "bind" module Foo: @@ -236,7 +304,7 @@ declarations, three of which are nested. `Bar` is the top-level group. `Baz` and `Qux` are nested under `Bar`. `Quz` is nested under `Qux`. ``` firrtl -circuit Foo: +circuit: declgroup Bar, bind: declgroup Baz, bind: declgroup Qux, bind: @@ -274,7 +342,7 @@ inside the group. _Stated differently, module `Baz` has an additional port `_a` that is only accessible inside a defined group `Bar`_. ``` firrtl -circuit Foo: +circuit: declgroup Bar, bind: module Baz: @@ -299,7 +367,7 @@ the nesting. E.g., the following circuit has a port associated with the nested group `Bar.Baz`: ``` firrtl -circuit Foo: +circuit: declgroup Bar, bind: declgroup Baz, bind: @@ -877,8 +945,8 @@ with inferring the input port as `AsyncReset`{.firrtl} if a direct connection was used: ```firrtl -circuit ResetInferBad : - module ResetInferBad : +circuit : + public module ResetInferBad : input in : Reset output out : AsyncReset connect out, read(probe(in)) @@ -888,8 +956,8 @@ The following circuit has all resets inferred to `AsyncReset`{.firrtl}, however: ```firrtl -circuit ResetInferGood : - module ResetInferGood : +circuit : + public module ResetInferGood : input in : Reset output out : Reset output out2 : AsyncReset @@ -961,8 +1029,8 @@ contexts so a single description of the module may be generated. The following example demonstrates such an invalid use of probe references: ```firrtl -circuit Top: - module Top: +circuit: + public module Top: input in : UInt<4> output out : UInt @@ -1119,7 +1187,7 @@ with some non-constant arguments produce a non-constant. Constants can be used in any context with a source flow which allows a non-constant. Constants may be used as the target of a connect so long as the source of the connect is itself constant. These rules ensure all constants are derived from constant integer -expressions or from constant-typed input ports of the top-level module. +expressions or from constant-typed input ports of a public module. ``` firrtl const UInt<3> @@ -1132,7 +1200,7 @@ control flow is conditioned on an expression which has a constant type. This means if a constant is being assigned to in a `when`{.firrtl} block, the `when`{.firrtl}'s condition must be a constant. -Output ports of external modules and input ports to the top-level module may be +Output ports of external modules and input ports to a public module may be constant. In such case, the value of the port is not known, but that it is non-mutating at runtime is known. @@ -2025,12 +2093,12 @@ example demonstrates creating an instance named `myinstance`{.firrtl} of the `MyModule`{.firrtl} module within the top level module `Top`{.firrtl}. ``` firrtl -circuit Top : +circuit : module MyModule : input a: UInt output b: UInt connect b, a - module Top : + public module Top : inst myinstance of MyModule ``` @@ -3416,11 +3484,12 @@ namespace. Annotations encode arbitrary metadata and associate it with zero or more targets ([@sec:targets]) in a FIRRTL circuit. -Annotations are represented as a dictionary, with a "class" field which -describes which annotation it is, and a "target" field which represents the IR -object it is attached to. Annotations may have arbitrary additional fields -attached. Some annotation classes extend other annotations, which effectively -means that the subclass annotation implies to effect of the parent annotation. +Annotations are represented as a dictionary, with a required "class" field which +describes which annotation it is. An annotation has an optional "target" field +which represents the IR object it is attached to. Annotations may have arbitrary +additional fields attached. Some annotation classes extend other annotations, +which effectively means that the subclass annotation implies to effect of the +parent annotation. Annotations are serializable to JSON. @@ -3429,7 +3498,15 @@ Below is an example annotation used to mark some module `foo`{.firrtl}: ```json { "class":"myannotationpackage.FooAnnotation", - "target":"~MyCircuit|MyModule>foo" + "target":"MyModule>foo" +} +``` + +Below is an example of an annotation which does not have a target: + +```json +{ + "class":"myannotationpackage.BarAnnotation" } ``` @@ -3440,18 +3517,17 @@ example, there may be multiple instances of a module which will eventually become multiple physical copies of that module on the die. Targets are a mechanism to identify specific hardware in specific instances of -modules in a FIRRTL circuit. A target consists of a circuit, a root module, an -optional instance hierarchy, and an optional reference. A target can only -identify hardware with a name, e.g., a circuit, module, instance, register, -wire, or node. References may further refer to specific fields or subindices in +modules in a FIRRTL circuit. A target consists of a root module, an optional +instance hierarchy, and an optional reference. A target can only identify +hardware with a name, e.g., a module, instance, register, wire, or +node. References may further refer to specific fields or subindices in aggregates. A target with no instance hierarchy is local. A target with an instance hierarchy is non-local. Targets use a shorthand syntax of the form: ```ebnf -target = “~” , circuit , - [ “|” , module , { “/” (instance) “:” (module) } , [ “>” , ref ] ] +target = module , [ { “/” (instance) “:” (module) } , [ “>” , ref ] ] ``` A reference is a name inside a module and one or more qualifying tokens that @@ -3469,7 +3545,7 @@ circuit. This consists of four instances of module `Baz`{.firrtl}, two instances of module `Bar`{.firrtl}, and one instance of module `Foo`{.firrtl}: ```firrtl -circuit Foo: +circuit: module Foo: inst a of Bar inst b of Bar @@ -3492,14 +3568,13 @@ representation where each instance is broken out into its own module. Using targets (or multiple targets), any specific module, instance, or combination of instances can be expressed. Some examples include: -Target Description ------------------------ ------------- -`~Foo` refers to the whole circuit -`~Foo|Foo` refers to the top module -`~Foo|Bar` refers to module `Bar`{.firrtl} (or both instances of module `Bar`{.firrtl}) -`~Foo|Foo/a:Bar` refers just to one instance of module `Bar`{.firrtl} -`~Foo|Foo/b:Bar/c:Baz` refers to one instance of module `Baz`{.firrtl} -`~Foo|Bar/d:Baz` refers to two instances of module `Baz`{.firrtl} +Target Description +------------------ ------------- +`Foo` refers to module `Foo`{.firrtl} (or the only instance of module `Foo`{.firrtl}) +`Bar` refers to module `Bar`{.firrtl} (or both instances of module `Bar`{.firrtl}) +`Foo/a:Bar` refers just to one instance of module `Bar`{.firrtl} +`Foo/b:Bar/c:Baz` refers to one instance of module `Baz`{.firrtl} +`Bar/d:Baz` refers to two instances of module `Baz`{.firrtl} If a target does not contain an instance path, it is a _local_ target. A local target points to all instances of a module. If a target contains an instance @@ -3517,11 +3592,11 @@ containing two annotations: [ { "class":"hello", - "target":"~Foo|Bar" + "target":"Bar" }, { "class":"world", - "target":"~Foo|Baz" + "target":"Baz" } ] ``` @@ -3531,14 +3606,14 @@ Annotation JSON in `%[ ... ]`{.firrtl}. The following shows the above annotation file stored in-line: ``` firrtl -circuit Foo: %[[ +circuit: %[[ { "class":"hello", - "target":"~Foo|Bar" + "target":"Bar" }, { "class":"world", - "target":"~Foo|Baz" + "target":"Baz" } ]] module Foo : @@ -3702,8 +3777,8 @@ As an example illustrating some of these points, the following is a legal FIRRTL circuit: ``` firrtl -circuit Foo : - module Foo : +circuit : + public module Foo : skip module Bar : input a: UInt<1> @@ -3723,8 +3798,8 @@ and '`\`' itself. The following example shows the info tokens included: ``` firrtl -circuit Top : @[myfile.txt 14:8] - module Top : @[myfile.txt 15:2] +circuit : @[myfile.txt 14:8] + public module Top : @[myfile.txt 15:2] output out: UInt @[myfile.txt 16:3] input b: UInt<32> @[myfile.txt 17:3] input c: UInt<1> @[myfile.txt 18:3] @@ -3991,7 +4066,7 @@ statement = (* Module definitions *) port = ( "input" | "output" ) , id , ":" , (type | type_property) , [ info ] ; -module = "module" , id , ":" , [ info ] , newline , indent , +module = [ "public" ] , "module" , id , ":" , [ info ] , newline , indent , { port , newline } , { statement , newline } , dedent ; @@ -4025,7 +4100,7 @@ version = "FIRRTL" , "version" , sem_ver ; (* Circuit definition *) circuit = version , newline , - "circuit" , id , ":" , [ annotations ] , [ info ] , newline , indent , + "circuit" , ":" , [ annotations ] , [ info ] , newline , indent , { module | extmodule | intmodule | declgroup | type_alias_decl } , dedent ; ```