From 84ccba734f236e7daf225218f546ddfbd01aa705 Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Fri, 20 Jan 2023 17:31:42 -0600 Subject: [PATCH 01/13] [major] Add reference types. --- include/firrtl.xml | 26 ++ revision-history.yaml | 1 + spec.md | 678 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 692 insertions(+), 13 deletions(-) diff --git a/include/firrtl.xml b/include/firrtl.xml index e6a6052a..41eab39a 100644 --- a/include/firrtl.xml +++ b/include/firrtl.xml @@ -18,6 +18,8 @@ of wire node + ref + define UInt @@ -28,6 +30,10 @@ Reset AsyncReset + + Probe + RWProbe + const @@ -81,11 +87,19 @@ assert assume cover + read + probe + rwprobe + force + release + force_initial + release_initial + @@ -108,6 +122,7 @@ + @@ -133,11 +148,21 @@ + + + + + + + + + + @@ -146,6 +171,7 @@ + diff --git a/revision-history.yaml b/revision-history.yaml index 935bdbc6..a5c5afa3 100644 --- a/revision-history.yaml +++ b/revision-history.yaml @@ -15,6 +15,7 @@ revisionHistory: - Render inline annotations as JSON, fix typo in example. - Fix rendering of type modifiers (const) in document. - Fix grammar for registers. + - Add reference types and related statements. # Information about the old versions. This should be static. oldVersions: - version: 1.2.0 diff --git a/spec.md b/spec.md index 050aaff7..189b8e36 100644 --- a/spec.md +++ b/spec.md @@ -218,9 +218,12 @@ Externally defined modules are modules whose implementation is not provided in the current circuit. Only the ports and name of the externally defined module are specified in the circuit. An externally defined module may include, in order, an optional _defname_ which sets the name of the external module in the -resulting Verilog and zero or more name--value _parameter_ statements. Each -name--value parameter statement will result in a value being passed to the named -parameter in the resulting Verilog. +resulting Verilog, zero or more name--value _parameter_ statements, and zero or +more _ref_ statements indicating the resolved paths of the module's exported +references. Each name--value parameter statement will result in a value being +passed to the named parameter in the resulting Verilog. While `ref` statements +are optional, they are required to be present if the reference is used. + An example of an externally defined module is: @@ -242,6 +245,22 @@ A common use of an externally defined module is to represent a Verilog module that will be written separately and provided together with FIRRTL-generated Verilog to downstream tools. +An example of an externally defined module with references is: + +```firrtl +extmodule MyExternalModuleWithRefs : + input foo : UInt<2> + output mysignal : Probe> + output myreg : RWProbe> + ref mysignal is "a.b" + ref myreg is "x.y" +``` +These resolved reference paths capture important information for use in the +current FIRRTL design. While they are part of the FIRRTL-level interface to the +external module, they are not expected to correspond to a particular Verilog +construct. They exist to carry information about the implementation of the +extmodule necessary for code generation of the current circuit. + ## Implementation Defined Modules (Intrinsics) Intrinsic modules are modules which represent implementation-defined, @@ -267,9 +286,11 @@ intmodule MyIntrinsicModule_xhello_y64 : # Types -FIRRTL has two classes of types: _ground_ types and _aggregate_ types. Ground -types are fundamental and are not composed of other types. Aggregate types are -composed of one or more aggregate or ground types. +FIRRTL has three classes of types: _ground_ types, _aggregate_ types, and +_reference_ types. Ground types are fundamental and are not composed of other +types. Aggregate types and reference types are composed of one or more +aggregate or ground types. Reference types may not contain other reference +types. ## Ground Types @@ -522,6 +543,214 @@ the module. The `c`{.firrtl} sub-field contained in the `b`{.firrtl} sub-field flows into the module, and the `d`{.firrtl} sub-field contained in the `b`{.firrtl} sub-field flows out of the module. + +## Reference Types + +References can be exported from a module for indirect access elsewhere, and are +captured using values of reference type. + +For use in cross-module references (hierarchical references in Verilog), a +reference to a probe of the circuit component is used. See [@sec:probes] for +details. + +Using reference-type ports, modules may expose internals for reading and +forcing without routing wires out of the design. + +This is often useful for testing and verification, where reference types allow +reads of the entities to be explicitly exported without hard-coding their place +in the design. Instead, by using references, a testbench module may express +accesses to the internals which will resolve to the appropriate target language +construct by the compiler (e.g., hierarchical reference). + +Reference ports are not expected to be synthesizable or representable in the +target language and are omitted in the compiled design; they only exist at the +FIRRTL level. + +Reference-type ports are statically routed through the design using the +`define`{.firrtl} statement. + +There are two reference types, `Probe`{.firrtl} and `RWProbe`{.firrtl}, +described below. These are used for indirect access to probes of the data +underlying circuit constructs they originate from, captured using +`probe`{.firrtl} expressions (see [@sec:probes]). + +`Probe`{.firrtl} types are read-only, and `RWProbe`{.firrtl} may be used with +`force`{.firrtl} and related statements. Prefer the former as much as +possible, as read-only probes impose fewer limitations and are more amenable to +optimization. + +References must always be able to be statically traced to their target, or to +an external module's output reference. This means no conditional connections +via sub-accesses, multiplexers, or other means. + +### Probe Types + +Probe types are reference types used to access circuit elements' data remotely. + +There are two probe types: `Probe`{.firrtl} and `RWProbe`{.firrtl}. +`RWProbe`{.firrtl} is a `Probe`{.firrtl} type, but not the other way around. + +Probe types are parametric over the type of data that they refer to, which is +always passive (as defined in [@sec:passive-types]) even when the probed target +is not (see [@sec:probes-and-passive-types]). Probe types cannot contain probe +types. + +Conceptually probe types are single-direction views of the probed data-flow +point. + +Examples: + +```firrtl +Probe ; readable reference to unsigned integer with inferred width +RWProbe<{x: {y: UInt}}> ; readable and forceable reference to bundle +``` + +For details of how to read and write through probe types, see +[@sec:reading-probe-references;@sec:force-and-release]. + +All ports of probe type must be initialized with exactly one `define`{.firrtl} +statement. + +Probe types are only allowed as part of module ports and may not appear +anywhere else. + +Sub-accesses are not allowed with types where the result is or has probe types +within. This is because sub-accesses are essentially conditional connections +(see [@sec:sub-accesses] for details), which are not allowed with probe types. +The following example demonstrates some legal and illegal expressions: +```firrtl +module NoSubAccessesWithProbes : + input x : {a : Probe, b : UInt}[3] + input i : UInt + input c : const UInt + output p : Probe + + ; Illegal: x[i], x[c] + ; Illegal: x[0].a[i], x[0].a[c] + + ; Legal: + define p = x[0].a[1] +``` + +Probe types may be specified as part of an external module (see +[@sec:externally-defined-modules]), with the resolved referent optionally +specified using `ref`{.firrtl} statements. + +### Input References + +Probe references are generally forwarded up the design hierarchy, being used to +reach down into design internals from a higher point. As a result probe-type +references are most often output ports, but may also be used on input ports +internally, as described in this section. + +Input probe references are allowed on internal modules, but they should be used +with care because they make it possible to express invalid reference paths. +When probe references are used to access the underlying data (e.g., with a +`read`{.firrtl} or `force`{.firrtl}), they must target a statically known +element at or below the point of that use. Support for other scenarios are +allowed as determined by the implementation. + +Input references are not allowed on public-facing modules: e.g., the top module +and external modules. + +Examples of input references follow. + +#### U-Turn Example + +```firrtl +module UTurn: + input in : Probe + output out : Probe + define out = in + +module RefBouncing: + input x: UInt + output y: UInt + + inst u1 of UTurn + inst u2 of UTurn + + node n = x + define u1.in = probe(n) + define u2.in = u1.out + + out <= read(u2.out) ; = x +``` + +In the above example, the probe of node `n`{.firrtl} is routed through two +modules before its resolution. + +#### Invalid Input Reference + +When using a probe reference, the target must reside at or below the point of use +in the design hierarchy. Input references make it possible to create designs +where this is not the case, and such upwards references are not supported: + +```firrtl +module Foo: + input in : Probe + output out : UInt + + out <= read(in) +``` + +#### IO with references to endpoint data + +A primary motivation for input references is that in some situations they make +it easier to generate the FIRRTL code. While output references necessarily +capture this design equivalently, this can be harder to generate and so is +useful to support. + +The following demonstrates an example of this, where it's convenient to use the +same bundle type as both output to one module and input to another, with +references populated by both modules targeting signals of interest at each end. +For this to be the same bundle type -- input on one and output on another -- +the `Probe` references for each end should be output-oriented and accordingly +are input-oriented at the other end. It would be inconvenient to generate this +design so that each has output references only. + +The `Connect` module instantiates a `Producer` and `Consumer` module, connects +them using a bundle with references in both orientations, and forwards those +references for inspection up the hierarchy. The probe targets are not +significant, here they are the same data being sent between the two, as stored +in each module. + +```firrtl +module Consumer: + input in : {a: UInt, pref: Probe, flip cref: Probe} + ; ... + node n = in.a + define in.cref = probe(n) + +module Producer: + output out : {a: UInt, pref: Probe, flip cref: Probe} + wire x : UInt + define out.pref = probe(x) + ; ... + out.a <= x + +module Connect: + output out : {x: Probe, y: Probe} + + inst a of A + inst b of B + + ; A => B + a.in.a <= b.out.a + define a.in.pref = b.out.pref + define b.out.cref = a.in.cref + + ; Send references out + define out.pref = b.out.pref + define out.cref = a.in.cref + +module Top: + inst c of Connect + + node producer_debug = read(c.pref); ; Producer-side signal + node consumer_debug = read(c.cref); ; Consumer-side signal +``` + ## Type Modifiers ### Constant Type @@ -890,6 +1119,9 @@ sub-element in the vector. Invalidating a component with a bundle type recursively invalidates each sub-element in the bundle. +Components of reference and analog type are ignored, as are any reference or +analog types within the component (as they cannot be connected to). + ## Attaches The `attach`{.firrtl} statement is used to attach two or more analog signals, @@ -1586,12 +1818,330 @@ en <= Z_valid cover(clk, pred, en, "X equals Y when Z is valid") : optional_name ``` +## Probes + +Probe references are created with `probe`{.firrtl} expressions, routed through +the design using the `define`{.firrtl} statement, read using the +`read`{.firrtl} expression (see [@sec:reading-probe-references]), and forced +and released with `force`{.firrtl} and `release`{.firrtl} statements. + +These statements are detailed below. + +### Define + +Define statements are used to route references through the design, and may be +used wherever is most convenient in terms of available identifiers -- their +location is not significant other than scoping, and do not have last-connect +semantics. Every sink-flow probe must be the target of exactly one of these +statements. + +The define statement takes a sink-flow static reference target and sets +it to the specified reference, which must either be a probe +expression, or a static reference source. + +Example: + +```firrtl +module Refs: + input clock: Clock + output a : Probe<{x: UInt, y: UInt}> ; read-only ref. to wire 'p' + output b : RWProbe ; force-able ref. to node 'q', inferred width. + output c : Probe> ; read-only ref. to register 'r' + output d : RWProbe>[4] ; vector of ref.'s to memory data in 'm' + output e : Probe ; ref. to input clock port + + wire p : {x: UInt, flip y : UInt} + define a = probe(p) ; probe is passive + node q = UInt<1>(0) + define b = rwprobe(q) + reg r: UInt, clock + define c = probe(r) + mem m: + data-type => UInt<5> + depth => 4 + ; ... + read-under-write => undefined + + define d = rwprobe(m) + define e = probe(clock) +``` + +The target is not required to be only an identifier, it may be a field within a +bundle or other statically known sub-element of an aggregate, for example: + +```firrtl +module Foo: + input x : UInt + output y : {x: UInt, p: Probe} + output z : Probe[2] + + wire w : UInt + w <= x + y.x <= w + + define y.p = probe(w) + define z[0] = probe(w) + define z[1] = probe(w) +``` + +`RWProbe`{.firrtl} references to ports are not allowed on public-facing +modules. + +#### Probes and Passive Types + +While `Probe`{.firrtl} inner types are passive, the type of the probed +static reference is not required to be: + +```firrtl +module Foo : + input x : {a: UInt, flip b: UInt} + output y : {a: UInt, flip b: UInt} + output xp : Probe<{a: UInt, b: UInt}> ; passive + + wire p : {a: UInt, flip b: UInt} ; p is not passive + define xp = probe(p) + p <= x + y <= p +``` + +#### Exporting References to Nested Declarations + +Nested declarations (see [@sec:nested-declarations]) may be exported: + +```firrtl +module RefProducer : + input a : UInt + input en : UInt<1> + output thereg : Probe + + when en : + reg myreg : UInt, clk + myreg <= a + define thereg = probe(myreg) +``` + +#### Forwarding References Upwards + +Define statements can be used to forward a child module's reference further up +the hierarchy: + +```firrtl +module Foo : + output p : Probe + ; ... + +module Forward : + output p : Probe + + inst f of Foo + define p = f.p +``` + +Define statements may narrow a probe of an aggregate to a sub-element using +static expression: + +```firrtl +module Foo : + output p : Probe[2] + ; ... + +module Forward : + output p : Probe + + inst f of Foo + define p = f.p[0][1] +``` + +#### Forwarding References Downwards + +Define statements can also be used to forward references down the hierarchy +using input reference-type ports, which are allowed but should be used +carefully as they make it possible to express +invalid reference paths. + +See [@sec:input-references] for more details, a small example is given below: + +```firrtl +module UnusedInputRef : + input r : Probe> + +module ForwardDownwards : + input in : UInt<1> + + inst u of UnusedInputRef + define u.r = probe(in) +``` + +### Force and Release + +To override existing drivers for a `RWProbe`{.firrtl}, the `force`{.firrtl} +statement is used, and released with `release`{.firrtl}. Force statements are +simulation-only constructs and may not be supported by all implementations. +They are similar to the verification statements (e.g., `assert`{.firrtl}) in +this regard. + +These are two variants of each, enumerated below: + +| Name | Arguments | Argument Types | +|-----------------|------------------------------|---------------------------------------- | +| force_initial | (ref, val) | (`RWProbe`{.firrtl}, T) | +| release_initial | (ref) | (`RWProbe`{.firrtl}) | +| force | (clock, condition, ref, val) | (`Clock`{.firrtl}, `UInt<1>`{.firrtl}, `RWProbe`{.firrtl}, T) | +| release | (clock, condition, ref) | (`Clock`{.firrtl}, `UInt<1>`{.firrtl}, `RWProbe`{.firrtl}) | + +Backends optionally generate corresponding constructs in the target language, +or issue an warning. + +The following `AddRefs`{.firrtl} module is used in the examples that follow for +each construct. + +```firrtl +module AddRefs: + output a : RWProbe> + output b : RWProbe> + output c : RWProbe> + output sum : UInt<3> + + node x = Uint<2>(0) + node y : UInt<2>(0) + node z : UInt<2>(0) + sum <= add(x, add(y, z)) + + define a = rwprobe(x) + define b = rwprobe(y) + define c = rwprobe(z) +``` + +#### Initial Force and Initial Release + +These variants force and release continuously: + +Example: + +```firrtl +module ForceAndRelease: + output o : UInt<3> + + inst r of AddRefs + o <= r.sum + + force_initial(r.a, 0) + force_initial(r.a, 1) + force_initial(r.b, 2) + force_initial(r.c, 3) + release_initial(r.c) +``` + +In this example, the output `o`{.firrtl} will be `3`. Note that globally the +last force statement overrides the others until another force or release +including the target. + +Sample SystemVerilog output for the force and release statements would be: + +```SystemVerilog +initial begin + force ForceAndRelease.AddRefs.x = 0; + force ForceAndRelease.AddRefs.x = 1; + force ForceAndRelease.AddRefs.y = 2; + force ForceAndRelease.AddRefs.z = 3; + release ForceAndRelease.AddRefs.z; +end +``` + +The `force_initial`{.firrtl} and `release_initial`{.firrtl} statements may +occur under `when`{.firrtl} blocks which becomes a check of the condition +first. Note that this condition is only checked once and changes to it +afterwards are irrelevant, and if executed the force will +continue to be active. For more control over their behavior, the other +variants should be used. Example: + +```firrtl +when c : force_initial(ref, x) + +would become: +```systemverilog +initial if (c) force a.b = x; +``` + +#### Force and Release + +These more detailed variants allow specifying a clock and condition for when +activating the force or release behavior continuously: + +```firrtl +module ForceAndRelease: + input a: UInt<2> + input clock : Clock + input cond : UInt<1> + output o : UInt<3> + + inst r of AddRefs + o <= r.sum + + force(clock, c, r.a, a) + release(clock, not(c), r.a) +``` + +Which at the positive edge of `clock`{.firrtl} will either force or release +`AddRefs.x`{.firrtl}. Note that once active, these remain active regardless of +the condition, until another force or release. + +Sample SystemVerilog output: + +```SystemVerilog +always @(posedge clock) begin + if (c) + force ForceAndRelease.AddRefs.x = a; + else + release ForceAndRelease.AddRefs.x; +end +``` + +Condition is checked in procedural block before the force, as shown above. +When placed under `when`{.firrtl} blocks, condition is mixed in as with other +statements (e.g., `assert`{.firrtl}). + +#### Non-Passive Force Target + +Force on a non-passive bundle drives in the direction of each field's +orientation. + +Example: + +```firrtl +module Top: + input x : {a: UInt, flip b: UInt} + output y : {a: UInt, flip b: UInt} + + inst d of DUT + d.x <= x + y <= d.y + + wire val : {a: UInt<2>, b: UInt<2>} + val.a <= UInt<2>(1) + val.b <= UInt<2>(2) + + ; Force takes a RWProbe and overrides the target with 'val'. + force(d.xp, val) + +module DUT : + input x : {a: UInt, flip b: UInt} + output y : {a: UInt, flip b: UInt} + output xp : RWProbe<{a: UInt, b: UInt}> + + ; Force drives p.a, p.b, y.a, and x.b, but not y.b and x.a + wire p : {a: UInt, flip b: UInt} + define xp = rwprobe(p) + p <= x + y <= p +``` + # Expressions FIRRTL expressions are used for creating literal unsigned and signed integers, for referring to a declared circuit component, for statically and dynamically -accessing a nested element within a component, for creating multiplexers, and -for performing primitive operations. +accessing a nested element within a component, for creating multiplexers, for +performing primitive operations, and for reading a remote reference to a probe. ## Unsigned Integers @@ -1717,6 +2267,16 @@ to refer to a reference expression to that component. Thus, the above example will be rewritten as "the port `in`{.firrtl} is connected to the port `out`{.firrtl}". +### Static Reference Expressions + +Static references start with an identifier, optionally followed by sub-fields +or sub-indices selecting a particular sub-element. Sub-accesses are not +allowed. + +Define statements must have static references as their target, and their source +must be either a static reference or a probe expression whose argument +is a static reference. + ## Sub-fields The sub-field expression refers to a sub-element of an expression with a bundle @@ -1957,6 +2517,82 @@ asClock(x) [@sec:primitive-operations] will describe the format and semantics of each primitive operation. +## Reading Probe References + +Probes are read using the `read`{.firrtl} operation. + +Read expressions have source flow and can be connected to other components: + +```firrtl +module Foo : + output p : Probe + ; ... + +module Bar : + output x : UInt + + inst f of Foo + x <= read(f.p) ; indirectly access the probed data +``` + +Indexing statically (sub-field, sub-index) into a probed value is allowed as +part of the read: + +```firrtl +module Foo : + output p : Probe<{a: UInt, b: UInt}> + ; ... + +module Bar : + output x : UInt + + inst f of Foo + x <= read(f.p.b) ; indirectly access the probed data +``` + +Read operations can be used anywhere a signal of the same underlying +type can be used, such as the following: + +```firrtl + x <= add(read(f.p).a, read(f.p).b) +``` + +The source of the probe must reside at or below the point of the +`read`{.firrtl} expression in the design hierarchy. See +[@sec:invalid-input-reference] for an example of an invalid `read`{.firrtl} of +an input reference. + +## Probe + +Probe references are generated with probe expressions. + +The probe expression creates a reference to a read-only or force-able view of +the data underlying the specified reference expression. + +The type of the produced probe reference is always passive, but the probed +expression may not be. + +Probed memories produce a vector of references to their data. + +There are two probe varieties: `probe`{.firrtl} and `rwprobe`{.firrtl} for +producing probes of type `Probe`{.firrtl} and `RWProbe`{.firrtl}, respectively. + +The following example exports a probe reference to a port: + +```firrtl +module MyModule : + input in: UInt + output r : Probe + + define r = probe(in) +``` + +The probed expression must be a static reference. + +See [@sec:probe-types;@sec:probe] for more details on probe references and +their use. + + # Primitive Operations {#sec:primitive-operations} The arguments of all primitive operations must be expressions with ground types, @@ -2770,8 +3406,9 @@ type_ground = "Clock" | "Reset" | "AsyncReset" | ( "UInt" | "SInt" | "Analog" ) , [ width ] ; type_aggregate = "{" , field , { field } , "}" | type , "[" , int , "]" ; +type_ref = ( "Probe" | "RWProbe" ) , "<", type , ">" ; field = [ "flip" ] , id , ":" , type ; -type = [ "const" ], ( type_ground | type_aggregate ) ; +type = ( [ "const" ] , ( type_ground | type_aggregate ) ) | type_ref ; (* Primitive operations *) primop_2expr_keyword = @@ -2802,11 +3439,15 @@ expr = ( "UInt" | "SInt" ) , [ width ] , "(" , ( int ) , ")" | reference | "mux" , "(" , expr , "," , expr , "," , expr , ")" + | "read" , "(" , static_reference , ")" | primop ; -reference = id - | reference , "." , id - | reference , "[" , int , "]" +static_reference = id + | static_reference , "." , id + | static_reference , "[" , int , "]" ; +reference = static_reference | reference , "[" , expr , "]" ; +ref_expr = ( "probe" | "rwprobe" ) , "(" , static_reference , ")" + | static_reference ; (* Memory *) ruw = ( "old" | "new" | "undefined" ) ; @@ -2821,6 +3462,13 @@ memory = "mem" , id , ":" , [ info ] , newline , indent , { "readwriter" , "=>" , id , newline } , dedent ; +(* Force and Release *) +force_release = + "force_initial" , "(" , ref_expr , "," , expr , ")" + | "release_initial" , "(" , ref_expr , ")" + | "force" , "(" , expr , "," , expr , "," , ref_expr , "," , expr , ")" + | "release" , "(" , expr , "," , expr , "," , ref_expr , ")" ; + (* Statements *) statement = "wire" , id , ":" , type , [ info ] | "reg" , id , ":" , type , expr , @@ -2839,7 +3487,9 @@ statement = "wire" , id , ":" , type , [ info ] | "stop(" , expr , "," , expr , "," , int , ")" , [ info ] | "printf(" , expr , "," , expr , "," , string , { expr } , ")" , [ ":" , id ] , [ info ] - | "skip" , [ info ] ; + | "skip" , [ info ] + | "define" , static_reference , "=" , ref_expr , [ info ] + | force_release , [ info ] ; (* Module definitions *) port = ( "input" | "output" ) , id , ":": , type , [ info ] ; @@ -2851,6 +3501,8 @@ extmodule = "extmodule" , id , ":" , [ info ] , newline , indent , { port , newline } , [ "defname" , "=" , id , newline ] , { "parameter" , id , "=" , ( string | int ) , newline } , + { "ref" , static_reference , "is" , + '"' , static_reference , '"' , newline } , dedent ; intmodule = "intmodule" , id , ":" , [ info ] , newline , indent , { port , newline } , From 43b1962009bf8d6679d1ffe94f3066f4efda3dbb Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Thu, 9 Mar 2023 14:17:08 -0600 Subject: [PATCH 02/13] Add text clarifying RWProbe and width/reset inference. --- spec.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/spec.md b/spec.md index 189b8e36..53e12201 100644 --- a/spec.md +++ b/spec.md @@ -636,6 +636,44 @@ Probe types may be specified as part of an external module (see [@sec:externally-defined-modules]), with the resolved referent optionally specified using `ref`{.firrtl} statements. +Probe types may target `const`{.firrtl} signals, but cannot use +`rwprobe`{.firrtl} with a constant signal to produce a +`RWProbe`{.firrtl}, as constant values should never be mutated at +runtime. + +### Width and Reset Inference + +Probe types do participate in global width and reset inference, but only in the +direction of the reference itself (no inference in the other direction, even +with force statements). Both inner types of the references used in a +`define`{.firrtl} statement must be identical or the same type with the +destination uninferred (this is checked recursively). Additionally, any +contained reset type is similarly only inferred in the direction of the +reference, even if it eventually reaches a known reset type. + +In the following example, the FIRRTL compiler will produce an error constrasted +with inferring the input port as `AsyncReset`{.firrtl} if a direct connection was used: + +```firrtl +circuit ResetInferBad : + module ResetInferBad : + input in : Reset + output out : AsyncReset + out <= read(probe(in)) +``` + +The following circuit has all resets inferred to `AsyncReset`{.firrtl}, however: + +```firrtl +circuit ResetInferGood : + module ResetInferGood : + input in : Reset + output out : Reset + output out2 : AsyncReset + out <= read(probe(in)) + out2 <= in +``` + ### Input References Probe references are generally forwarded up the design hierarchy, being used to From 4bcc9536fc54c8270334a863fcac4ef8a69c1333 Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Thu, 9 Mar 2023 14:22:09 -0600 Subject: [PATCH 03/13] touchup header depth, add text about what can be "define"d. --- spec.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/spec.md b/spec.md index 53e12201..4ac87a62 100644 --- a/spec.md +++ b/spec.md @@ -641,7 +641,7 @@ Probe types may target `const`{.firrtl} signals, but cannot use `RWProbe`{.firrtl}, as constant values should never be mutated at runtime. -### Width and Reset Inference +#### Width and Reset Inference Probe types do participate in global width and reset inference, but only in the direction of the reference itself (no inference in the other direction, even @@ -1873,9 +1873,10 @@ location is not significant other than scoping, and do not have last-connect semantics. Every sink-flow probe must be the target of exactly one of these statements. -The define statement takes a sink-flow static reference target and sets -it to the specified reference, which must either be a probe -expression, or a static reference source. +The define statement takes a sink-flow static reference target and sets it to +the specified reference, which must either be a probe expression, or a static +reference source. + Example: @@ -1925,6 +1926,13 @@ module Foo: `RWProbe`{.firrtl} references to ports are not allowed on public-facing modules. +Define statements can set a `Probe`{.firrtl} to either a `Probe`{.firrtl} or +`RWProbe`{.firrtl}, but a `RWProbe`{.firrtl} cannot be set to a +`Probe`{.firrtl}. The inner types of the two references must (recursively) be +identical or identical with the destination containing uninferred versions of +the corresponding element in the source type. See +[@sec:width-and-reset-inference] for details. + #### Probes and Passive Types While `Probe`{.firrtl} inner types are passive, the type of the probed From 8aa183c81cf75a03d984d226c308f9b44d0e1e95 Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Thu, 9 Mar 2023 15:15:01 -0600 Subject: [PATCH 04/13] Drop probe of memory. We can implement more or less the same idea internally, but defining it in terms of the abstract memories in FIRRTL is not reasonable and unlikely to be generally implementable. This can be added in the future as needed and reasonable semantics are determined for this situation. --- spec.md | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/spec.md b/spec.md index 4ac87a62..7a1d35b2 100644 --- a/spec.md +++ b/spec.md @@ -1886,8 +1886,7 @@ module Refs: output a : Probe<{x: UInt, y: UInt}> ; read-only ref. to wire 'p' output b : RWProbe ; force-able ref. to node 'q', inferred width. output c : Probe> ; read-only ref. to register 'r' - output d : RWProbe>[4] ; vector of ref.'s to memory data in 'm' - output e : Probe ; ref. to input clock port + output d : Probe ; ref. to input clock port wire p : {x: UInt, flip y : UInt} define a = probe(p) ; probe is passive @@ -1895,14 +1894,7 @@ module Refs: define b = rwprobe(q) reg r: UInt, clock define c = probe(r) - mem m: - data-type => UInt<5> - depth => 4 - ; ... - read-under-write => undefined - - define d = rwprobe(m) - define e = probe(clock) + define d = probe(clock) ``` The target is not required to be only an identifier, it may be a field within a @@ -2616,9 +2608,8 @@ The probe expression creates a reference to a read-only or force-able view of the data underlying the specified reference expression. The type of the produced probe reference is always passive, but the probed -expression may not be. - -Probed memories produce a vector of references to their data. +expression may not be. Memories and their ports are not supported as probe +targets. There are two probe varieties: `probe`{.firrtl} and `rwprobe`{.firrtl} for producing probes of type `Probe`{.firrtl} and `RWProbe`{.firrtl}, respectively. From 386df56e3632b8546e50d7875a91596a59b7480e Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Mon, 13 Mar 2023 17:37:28 -0500 Subject: [PATCH 05/13] Touchup nested example, add missing port, set width on input. --- spec.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec.md b/spec.md index 7a1d35b2..3e324940 100644 --- a/spec.md +++ b/spec.md @@ -1948,8 +1948,9 @@ Nested declarations (see [@sec:nested-declarations]) may be exported: ```firrtl module RefProducer : - input a : UInt + input a : UInt<4> input en : UInt<1> + input clk : Clock output thereg : Probe when en : From c13b460332c1627db4cdf5313694cae21c0624d1 Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Mon, 13 Mar 2023 17:44:37 -0500 Subject: [PATCH 06/13] Fixup "AddRefs" example, capitalization typo, node init syntax. --- spec.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec.md b/spec.md index 3e324940..cd0dd7a3 100644 --- a/spec.md +++ b/spec.md @@ -2041,9 +2041,9 @@ module AddRefs: output c : RWProbe> output sum : UInt<3> - node x = Uint<2>(0) - node y : UInt<2>(0) - node z : UInt<2>(0) + node x = UInt<2>(0) + node y = UInt<2>(0) + node z = UInt<2>(0) sum <= add(x, add(y, z)) define a = rwprobe(x) From 79b990d6a0711bad4022bfefe6815755cdd2bcb7 Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Tue, 14 Mar 2023 20:10:38 -0500 Subject: [PATCH 07/13] ref statement as mandatory for now. This is simpler and omitting when unused is unnecessarily complicated. Omitting for use with FIRRTL<-->FIRRTL "linking" or resolving across components would have utility, but not currently planned so just make their presence mandatory. --- spec.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec.md b/spec.md index cd0dd7a3..5e6c654f 100644 --- a/spec.md +++ b/spec.md @@ -221,9 +221,8 @@ order, an optional _defname_ which sets the name of the external module in the resulting Verilog, zero or more name--value _parameter_ statements, and zero or more _ref_ statements indicating the resolved paths of the module's exported references. Each name--value parameter statement will result in a value being -passed to the named parameter in the resulting Verilog. While `ref` statements -are optional, they are required to be present if the reference is used. - +passed to the named parameter in the resulting Verilog. Every port or port +sub-element of reference type must have exactly one `ref`{.firrtl} statement. An example of an externally defined module is: From 9f6697b252e1429f99bb0fdb5fe4a1a35f7a7246 Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Wed, 15 Mar 2023 16:52:09 -0500 Subject: [PATCH 08/13] Clarify input probe refs also can't be context-dependent. References must be static for the module, not static for that instance. --- spec.md | 53 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/spec.md b/spec.md index 5e6c654f..85a4f107 100644 --- a/spec.md +++ b/spec.md @@ -681,11 +681,11 @@ references are most often output ports, but may also be used on input ports internally, as described in this section. Input probe references are allowed on internal modules, but they should be used -with care because they make it possible to express invalid reference paths. -When probe references are used to access the underlying data (e.g., with a -`read`{.firrtl} or `force`{.firrtl}), they must target a statically known -element at or below the point of that use. Support for other scenarios are -allowed as determined by the implementation. +with care because they make it possible to express invalid or multiple +reference paths. When probe references are used to access the underlying data +(e.g., with a `read`{.firrtl} or `force`{.firrtl}), they must target a +statically known element at or below the point of that use, in all contexts. +Support for other scenarios are allowed as determined by the implementation. Input references are not allowed on public-facing modules: e.g., the top module and external modules. @@ -725,10 +725,47 @@ where this is not the case, and such upwards references are not supported: ```firrtl module Foo: - input in : Probe - output out : UInt + input in : Probe + output out : UInt + + out <= read(in) +``` - out <= read(in) +Even when the target resolves at or below, the path must be the same in all +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: + input in : UInt<4> + output out : UInt + + inst ud1 of UpDown + ud1.in <= in + define ud1.in_ref = ud1.r1 + + inst ud2 of UpDown + ud2.in <= in + define ud2.in_ref = ud2.r2 + + out <= add(ud1.out, ud2.out) + + module UpDown: + input in : UInt<4> + input in_ref : Probe> + output r1 : Probe> + output r2 : Probe> + output out : UInt + + ; In ud1, this is UpDown.n1, in ud2 this is UpDown.n2 . + ; However, this is not supported as it cannot be both at once. + out <= read(in_ref) + node n1 = and(in, UInt<4>(1)) + node n2 = and(in, UInt<4>(3)) + define r1 = probe(n1) + define r2 = probe(n2) ``` #### IO with references to endpoint data From dfceec7fe3274157954f39701b22504b02e6441d Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Wed, 15 Mar 2023 17:27:48 -0500 Subject: [PATCH 09/13] Clarify references != probes a bit more. --- spec.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec.md b/spec.md index 85a4f107..bccdc060 100644 --- a/spec.md +++ b/spec.md @@ -552,14 +552,14 @@ For use in cross-module references (hierarchical references in Verilog), a reference to a probe of the circuit component is used. See [@sec:probes] for details. -Using reference-type ports, modules may expose internals for reading and -forcing without routing wires out of the design. +Using probe-type ports, modules may expose internals for reading and forcing +without routing wires out of the design. -This is often useful for testing and verification, where reference types allow +This is often useful for testing and verification, where probe types allow reads of the entities to be explicitly exported without hard-coding their place -in the design. Instead, by using references, a testbench module may express -accesses to the internals which will resolve to the appropriate target language -construct by the compiler (e.g., hierarchical reference). +in the design. Instead, by using probe-type references, a testbench module may +express accesses to the internals which will resolve to the appropriate target +language construct by the compiler (e.g., hierarchical reference). Reference ports are not expected to be synthesizable or representable in the target language and are omitted in the compiled design; they only exist at the From 35a46684f189459af8f44f54e088da3db8189512 Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Wed, 15 Mar 2023 17:39:58 -0500 Subject: [PATCH 10/13] Touchup language differentiating probe-types from references generally. --- spec.md | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/spec.md b/spec.md index bccdc060..ed07b828 100644 --- a/spec.md +++ b/spec.md @@ -578,7 +578,7 @@ underlying circuit constructs they originate from, captured using possible, as read-only probes impose fewer limitations and are more amenable to optimization. -References must always be able to be statically traced to their target, or to +Probe references must always be able to be statically traced to their target, or to an external module's output reference. This means no conditional connections via sub-accesses, multiplexers, or other means. @@ -591,11 +591,12 @@ There are two probe types: `Probe`{.firrtl} and `RWProbe`{.firrtl}. Probe types are parametric over the type of data that they refer to, which is always passive (as defined in [@sec:passive-types]) even when the probed target -is not (see [@sec:probes-and-passive-types]). Probe types cannot contain probe -types. +is not (see [@sec:probes-and-passive-types]). Probe types cannot contain +reference types. Conceptually probe types are single-direction views of the probed data-flow -point. +point. They are references to the data accessed with the probe expression +generating the reference. Examples: @@ -673,7 +674,7 @@ circuit ResetInferGood : out2 <= in ``` -### Input References +### Input Probe References Probe references are generally forwarded up the design hierarchy, being used to reach down into design internals from a higher point. As a result probe-type @@ -687,10 +688,10 @@ reference paths. When probe references are used to access the underlying data statically known element at or below the point of that use, in all contexts. Support for other scenarios are allowed as determined by the implementation. -Input references are not allowed on public-facing modules: e.g., the top module +Input probe references are not allowed on public-facing modules: e.g., the top module and external modules. -Examples of input references follow. +Examples of input probe references follow. #### U-Turn Example @@ -770,10 +771,10 @@ circuit Top: #### IO with references to endpoint data -A primary motivation for input references is that in some situations they make -it easier to generate the FIRRTL code. While output references necessarily -capture this design equivalently, this can be harder to generate and so is -useful to support. +A primary motivation for input probe references is that in some situations they +make it easier to generate the FIRRTL code. While output references +necessarily capture this design equivalently, this can be harder to generate +and so is useful to support. The following demonstrates an example of this, where it's convenient to use the same bundle type as both output to one module and input to another, with @@ -781,7 +782,7 @@ references populated by both modules targeting signals of interest at each end. For this to be the same bundle type -- input on one and output on another -- the `Probe` references for each end should be output-oriented and accordingly are input-oriented at the other end. It would be inconvenient to generate this -design so that each has output references only. +design so that each has output probe references only. The `Connect` module instantiates a `Producer` and `Consumer` module, connects them using a bundle with references in both orientations, and forwards those @@ -1910,8 +1911,8 @@ semantics. Every sink-flow probe must be the target of exactly one of these statements. The define statement takes a sink-flow static reference target and sets it to -the specified reference, which must either be a probe expression, or a static -reference source. +the specified reference, which must either be a compatible probe expression or +static reference source. Example: @@ -2034,7 +2035,7 @@ using input reference-type ports, which are allowed but should be used carefully as they make it possible to express invalid reference paths. -See [@sec:input-references] for more details, a small example is given below: +See [@sec:input-probe-references] for more details, a small example is given below: ```firrtl module UnusedInputRef : From 3e0fd193e59379dda29f96b9aea63522225176e9 Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Wed, 15 Mar 2023 17:41:36 -0500 Subject: [PATCH 11/13] [NFC] (reflow) --- spec.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec.md b/spec.md index ed07b828..edc9260a 100644 --- a/spec.md +++ b/spec.md @@ -578,9 +578,9 @@ underlying circuit constructs they originate from, captured using possible, as read-only probes impose fewer limitations and are more amenable to optimization. -Probe references must always be able to be statically traced to their target, or to -an external module's output reference. This means no conditional connections -via sub-accesses, multiplexers, or other means. +Probe references must always be able to be statically traced to their target, +or to an external module's output reference. This means no conditional +connections via sub-accesses, multiplexers, or other means. ### Probe Types @@ -688,8 +688,8 @@ reference paths. When probe references are used to access the underlying data statically known element at or below the point of that use, in all contexts. Support for other scenarios are allowed as determined by the implementation. -Input probe references are not allowed on public-facing modules: e.g., the top module -and external modules. +Input probe references are not allowed on public-facing modules: e.g., the top +module and external modules. Examples of input probe references follow. From b8021d7268818ad775f4b09ff55ebdef62a11a1d Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Wed, 15 Mar 2023 17:48:23 -0500 Subject: [PATCH 12/13] touchup stale language re:optional ref statement --- spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec.md b/spec.md index edc9260a..8d72d4b2 100644 --- a/spec.md +++ b/spec.md @@ -633,7 +633,7 @@ module NoSubAccessesWithProbes : ``` Probe types may be specified as part of an external module (see -[@sec:externally-defined-modules]), with the resolved referent optionally +[@sec:externally-defined-modules]), with the resolved referent for each specified using `ref`{.firrtl} statements. Probe types may target `const`{.firrtl} signals, but cannot use From 6c516f797e6f37a2f5b930f0ec1ab4da521dca46 Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Wed, 15 Mar 2023 17:48:31 -0500 Subject: [PATCH 13/13] [NFC] (reflow) --- spec.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec.md b/spec.md index 8d72d4b2..bf7cb3a0 100644 --- a/spec.md +++ b/spec.md @@ -652,7 +652,8 @@ contained reset type is similarly only inferred in the direction of the reference, even if it eventually reaches a known reset type. In the following example, the FIRRTL compiler will produce an error constrasted -with inferring the input port as `AsyncReset`{.firrtl} if a direct connection was used: +with inferring the input port as `AsyncReset`{.firrtl} if a direct connection +was used: ```firrtl circuit ResetInferBad : @@ -662,7 +663,8 @@ circuit ResetInferBad : out <= read(probe(in)) ``` -The following circuit has all resets inferred to `AsyncReset`{.firrtl}, however: +The following circuit has all resets inferred to `AsyncReset`{.firrtl}, +however: ```firrtl circuit ResetInferGood :