From e22cde99cadfdae351be815e263fc2799fe8e4bc Mon Sep 17 00:00:00 2001 From: Ned Bingham Date: Tue, 24 Sep 2024 22:41:49 -0400 Subject: [PATCH] adding language documentation --- docs/_lang/chp.md | 195 +++++++++++++++++++++++----------------------- docs/_lang/hse.md | 70 +++++++++++++++++ docs/_lang/prs.md | 119 ++++++++++++++++++++++++++++ 3 files changed, 287 insertions(+), 97 deletions(-) create mode 100644 docs/_lang/hse.md create mode 100644 docs/_lang/prs.md diff --git a/docs/_lang/chp.md b/docs/_lang/chp.md index 00fd190..3f0ed6c 100644 --- a/docs/_lang/chp.md +++ b/docs/_lang/chp.md @@ -1,145 +1,146 @@ --- -title: Communicating Hardware Processes (CHP) +title: Communicating Hardware Processes author: Edward Bingham date: 2024-09-24 category: Language layout: post --- - -HSE stands for Handshaking Expansions. It is a step in between Communicating -Hardware Processes (CHP) and Production Rules (PRs). Its a control flow -language where all actions are limited to 1 bit boolean. There are only a few -basic syntax structures most of which are composition operators. Spacing is -ignored during parsing. The following list explains what each syntax does. -Composition operators are listed by precedence from weakest to strongest. -------------------------------------------------------------------------------- +**Communicating Hardware Processes (CHP)** is a program notation for QDI +circuits inspired by Tony Hoare's communicating sequential processes +(CSP) and Edsger W. Dijkstra's guarded commands. For example, this is how one +might write an adder with sources for the input channels `A` and `B` and a sink +for the output channel `S`. The environment is placed in a separate isochronic +region. ``` -skip +*[ A?a, B?b; S!(a+b) ] || +( + *[ A!rand() ] || + *[ B!rand() ] || + *[ S? ] +)'1 ``` -This is just a no-op. +The syntax is described below in descending precedence. Spacing is +ignored during parsing. -------------------------------------------------------------------------------- +## Data Types -``` -x- -x+ -``` +There are currently only three data types supported by Loom. A **node** +is a single wire in the circuit. A **variable** is an integer type +encoded on a collection of nodes. A **channel** facilitates +communication between processes with a request, acknowledge or request, +enable protocol. Currently, data types are implied by the operators on them. -Every variable in HSE represents a node in the circuit. `x-` sets the voltage -on that node to GND and `x+` sets the voltage on that node to VDD. +## Skip -------------------------------------------------------------------------------- +`skip` skips to the next action. -``` -P0 || P1 || ... || Pn -``` +## Assignment -Parallel composition: do `P0`, `P1`, ..., and `Pn` in any interleaving. +`a+` sets the voltage of the **node** `a` to Vdd while `a-` sets the voltage of +`a` to GND. `b := e` evaluates the expression `e` then assigns the resulting +value to the **variable** `b`. -------------------------------------------------------------------------------- +## Channel Operators -``` -P0;P1;...;Pn -``` +**Send** `X!e` evaluates the expression `e` then sends the resulting value +across the **channel** `X`. `X!` is a dataless send. -Sequential composition: do `P0`, then `P1`, then ..., then `Pn`. +**Receive** `X?a` waits until there is a valid value on the **channel** `X` +then assigns that value to the **variable** `a`. `X?` is a dataless receive. -------------------------------------------------------------------------------- +**Probe** `#X` returns the value waiting on the **channel** `X` without +executing the receive. -``` -P0,P1,...,Pn -``` +## Parallel Composition -Internal parallel composition is the same as parallel composition. +`P0 || P1 || ... || Pn` executes the processes `P0`, `P1`, ..., and `Pn` in +parallel. Keep in mind the difference between "parallel" and "concurrent". -------------------------------------------------------------------------------- +* **parallel** two parallel processes can execute in any order or at the same time. +* **concurrent** two concurrent processes can execute in any order. -``` -[ G0 -> P0 -[] G1 -> P1 -... -[] Gn -> Pn -] +## Sequential Composition -[ G0 -> P0 -: G1 -> P1 -... -: Gn -> Pn -] +`P0;P1;...;Pn` executes first executes the process `P0`, then `P1`, then ..., then `Pn`. -[G] -``` +## Internal Parallel Composition -The selection composition represents choice. `G0`, `G1`, ..., `Gn` are called guards. -They are boolean expressions that represent the condition of the selection and -`P0`, `P1`, ..., `Pn` are the processes that are executed for each condition. +`P0,P1,...,Pn` is the same as parallel composition, but with higher precendence. -A selection statement can either be deterministic as represented by the thick -bar operator `[]` or non-deterministic as represented by the thin bar operator -`:`. If it is a deterministic selection, then the guards are guaranteed by the -user to be mutually exclusive so only one can ever evaluate to true at any -given time. Meanwhile if it is non-deterministic, then an arbiter or in -some extreme cases, a synchronizer, must be used to guarantee the mutual -exclusion of the selection. Ultimately the selection operator implements the -following: +## Wait -If `G0` do `P0`, if `G1` do `P1`, ... If `Gn` do `Pn`, else wait. +A **guard** is a dataless [[boolean expression] or expression that is +implicitly cast using a validity check on the encoding. The guard evaluates to +`Vdd` when the data is valid and `GND` when it is neutral. -If there is no process specified as in the third example, then the process -is just a `skip`. This is shorthand for a wait until operation, also -known simply as a 'guard'. +`[G]` waits for the guard `G` to evaluate to `Vdd` before continuing. -------------------------------------------------------------------------------- +## Selection +The selection composition represents choice. The guard `G0`, `G1`, ..., `Gn` +represent the condition of the selection and `P0`, `P1`, ..., `Pn` are the +processes that are executed for each condition. + +A deterministic selection statement is represented by the thick bar operator +`[]`. The guards are guaranteed by the user to be mutually exclusive so only +one can ever evaluate to true at any given time. The tooling will throw an +error if a deterministic selection violates this mutual exclusion constraint. ``` -*[ G0 -> P0 +[ G0 -> P0 [] G1 -> P1 ... [] Gn -> Pn ] +``` -*[ G0 -> P0 +A non-deterministic selection state is represented by the thin bar operator +`:`. An arbiter or in some extreme cases, a synchronizer, will be used to +guarantee the mutual exclusion of the selection. +``` +[ G0 -> P0 : G1 -> P1 ... : Gn -> Pn ] - -*[P] ``` -Repetitive selection behaves almost the same as the non-repetitive selection -statement. Think of it like a while loop. +Ultimately the selection operator implements the following: +If `G0` do `P0`, if `G1` do `P1`, ... If `Gn` do `Pn`, else wait. -While one of the guards `(G0,G1,...,Gn)` is true, execute the associated process -`(P0,P1,...,Pn)`. Else, exit the loop. +## Repetition -If the guard is not specified, then the guard is assumed to be '1'. This -is shorthand for a loop that will never exit. +Repetition behaves almost the same as the non-repetitive selection +statement. Think of it like a while loop. While one of the guards +`(G0,G1,...,Gn)` is true, execute the associated process `(P0,P1,...,Pn)`. +Else, exit the loop. -## Internal Representation of State +Repetition can either be deterministic as represented by the thick bar. +``` +*[ G0 -> P0 + [] G1 -> P1 + ... + [] Gn -> Pn + ] +``` -The state of a node is represented by four basic values `(-,0,1,X)`. `-` means -that the node is stable at either GND or VDD but the process doesn't know -which. `0` means the node is stable at GND. `1` means the node is stable at -VDD. And `X` means the node is unstable or metastable (some unknown value -between GND and VDD). +Or it can be non-deterministic as represented by the thin bar. +``` +*[ G0 -> P0 + : G1 -> P1 + ... + : Gn -> Pn + ] +``` -`a+` drives the node `a` to `1` and `a-` drives the node `a` to `0`. If two -assignments interfere as in `a+,a-`, then the value of the node `a` will -be driven to `X`. If an assignment is unstable as in `[a];b+||a-`, then the -node `b` will be drive to `X`. +`*[S]` is shorthand for `*[Vdd -> S]` and implements infinite repetition. -If there is a selection like `[1->a+:1->a-];b-`, then at the semicolon before -`b-`, the value of the node `a` will be `-`. (Yes I know this example does not -represent a real circuit because you don't know when the selection has -completed). +## Timing Assumption -If a node has a value of `X`, then it will propagate as expected. For example -in `b-; [a]; b+` if the node `a` is unstable, then after `b+`, the node `b` will -also be unstable. +`{G}` blocks as long as G evaluates to GND. Instabilities on `G` are not +propagated out into the rest of the circuit. ## Isochronic Regions @@ -173,18 +174,18 @@ x'1+ ``` If there are two processes in two isochronic regions like `(a-;b+;P)'0 || ([b]; a+)'1`, -then during the process `P`, the value of the node `a` will be `-` because the -process on the left knows that `a` was `0` but that it will change to `1`. It +then during the process `P`, the value of the node `a` will be unknown because the +process on the left knows that `a` was `GND` but that it will change to `Vdd`. It just doesn't know when. Meanwhile in the process on the right, the value of `a` -will start at `-` and transition to `1` after the assignment `a+`. +will start unknown and transition to `Vdd` after the assignment `a+`. ## Reset Behavior Because reset behavior can be a complex thing that has a multitude of timing -assumptions and different possible implementations, hsesim has a very basic -reset implementation. It goes as follows: as long as there isn't any choice to -be made, and we don't enter a loop, execute transitions and accumulate their -affect on the state into a reset state. Here is an example: +assumptions and different possible implementations, Loom has a very basic +reset implementation. As long as there isn't any choice to be made, and we +don't enter a repetition, execute transitions and accumulate their affect on +the state into a reset state. Here is an example: ``` R.f-,R.t-,L.e+; [R.e&~L.f&~L.t]; diff --git a/docs/_lang/hse.md b/docs/_lang/hse.md new file mode 100644 index 0000000..444a88d --- /dev/null +++ b/docs/_lang/hse.md @@ -0,0 +1,70 @@ +--- +title: Hand Shaking Expansions +author: Edward Bingham +date: 2024-09-24 +category: Language +layout: post +--- + +**Hand Shaking Expansions (HSE)** are a subset of CHP in which channel +protocols are expanded into guards and assignments and only dataless +operators are permitted. This is an intermediate representation toward +the synthesis of QDI circuits. A process in HSE should at most represent a +single pipeline stage. For example, this is how one might write a 1-bit adder +with sources for the three input channels `A`, `B`, and `Ci` and sinks for the +two output channels `S` and `Co`. The environment is placed in a separate +isochronic region. + +``` +S.f-,S.t-,Co.f-,Co.t-,ABCi.e+; [S.e&Co.e&~A.f&~A.t&~B.f&~B.t&~Ci.f&~Ci.t]; +*[ + ( + [ S.e & (A.t & B.f & Ci.f | A.f & B.t & Ci.f | A.f & B.f & Ci.t | A.t & B.t & Ci.t) -> S.t+ + [] S.e & (A.t & B.t & Ci.f | A.t & B.f & Ci.t | A.f & B.t & Ci.t | A.f & B.f & Ci.f) -> S.f+ + ] || + [ Co.e & (A.t & B.t & Ci.f | A.t & B.f & Ci.t | A.f & B.t & Ci.t | A.t & B.t & Ci.t) -> Co.t+ + [] Co.e & (A.t & B.f & Ci.f | A.f & B.t & Ci.f | A.f & B.f & Ci.t | A.f & B.f & Ci.f) -> Co.f+ + ] + ); ABCi.e-; [~A.t & ~A.f & ~B.t & ~B.f & ~Ci.t & ~Ci.f]; + ( + [~S.e -> S.t-,S.f-] || + [~Co.e -> Co.t-,Co.f-] + ); + ABCi.e+ +] || + +(S.e+; [~S.f&~S.t]; *[[S.t | S.f]; S.e-; [~S.t & ~S.f]; S.e+] || + +Co.e+; [~Co.f&~Co.t]; *[[Co.t | Co.f]; Co.e-; [~Co.t & ~Co.f]; Co.e+] || + +A.f-,A.t-; [ABCi.e]; +*[[1->A.t+:1->A.f+]; [~ABCi.e]; A.t-,A.f-; [ABCi.e]] || + +B.f-,B.t-; [ABCi.e]; +*[[1->B.t+:1->B.f+]; [~ABCi.e]; B.t-,B.f-; [ABCi.e]] || + +Ci.f-,Ci.t-; [ABCi.e]; +*[[1->Ci.t+:1->Ci.f+]; [~ABCi.e]; Ci.t-,Ci.f-; [ABCi.e]])'1 +``` + +## Internal Representation of State + +The state of a node is represented by four basic values `(-,0,1,X)`. `-` means +that the node is stable at either GND or VDD but the process doesn't know +which. `0` means the node is stable at GND. `1` means the node is stable at +VDD. And `X` means the node is unstable or metastable (some unknown value +between GND and VDD). + +`a+` drives the node `a` to `1` and `a-` drives the node `a` to `0`. If two +assignments interfere as in `a+,a-`, then the value of the node `a` will +be driven to `X`. If an assignment is unstable as in `[a];b+||a-`, then the +node `b` will be drive to `X`. + +If there is a selection like `[1->a+:1->a-];b-`, then at the semicolon before +`b-`, the value of the node `a` will be `-`. (Yes I know this example does not +represent a real circuit because you don't know when the selection has +completed). + +If a node has a value of `X`, then it will propagate as expected. For example +in `b-; [a]; b+` if the node `a` is unstable, then after `b+`, the node `b` will +also be unstable. diff --git a/docs/_lang/prs.md b/docs/_lang/prs.md new file mode 100644 index 0000000..1545ce3 --- /dev/null +++ b/docs/_lang/prs.md @@ -0,0 +1,119 @@ +--- +title: Production Rule Set +author: Edward Bingham +date: 2024-09-24 +category: Language +layout: post +--- + +A production rule specifies either the pull-up or pull-down network of a gate +in a QDI circuit. Many implicit inverters can be automatically added, feedback +for stateholding or non-combinational gates is automatically generated, and +sizing is automatically computed. For example a simple production rule set for +a dataless buffer with a source for the input channel `L` and a sink for the +output channel `R` would look like this. + +``` +require driven, stable, noninterfering +_Reset & R.e & L.r -> R.r+ +R.r -> L.e- +~_Reset | ~R.e & ~L.r -> R.r- +~R.r -> L.e+ + +L.e -> L.r+ +~L.e -> L.r- + +R.r -> R.e- +~R.r -> R.e+ +``` + +## Production Rule + +The syntax `G -> S` has a **guard** `G` and **dataless assignment** `S`. In +states not covered by the guards, it is assumed that the assigned nodes remain +at their previous states. This can be achieved using a keeper using either weak +or combinational feedback. The most basic example is the C-element in which the +guards do not cover the states where the two inputs are not the same value. + +## Require and Assume + +At the top of the production rule file, you may specify a comma +separated list of global behavioral constraints and assumptions. The +following are supported: + +``` +assume nobackflow, static +require driven, stable, noninterfering, adiabatic +``` + +* **assume nobackflow** - NMOS transistors no longer drives a weak 1 and PMOS +transistors no longer drive a weak 0. +* **assume static** - Undriven nodes keep their value instead of drifting to +`X`. The strength still drifts to undriven. +* **require driven** - An error is thrown when a floating node is detected. +* **require stable** - An error is thrown when a glitch is detected. +* **require noninterfering** - An error is thrown when a short is detected. +* **require adiabatic** - An error is thrown when a transition on the gate of a +transistor connects source to drain when they are not already the same +voltage. + +## Pass Transistor Logic + +The source node of guard may be specified using +`@source & G -> S`. This allows you to efficiently write pass transistor logic +or shared gate networks. + +## Anonymous Nodes + +Anonymous nodes must follow the naming pattern `_0` +where `0` can be any integer number. + +## Flags + +Behaviors for specific production rules can be tuned using various +flags as follows `G -> S [flag...]`. The following flags are supported. + +* **keep** - This net holds its value when it's not driven. We're assuming a +keeper on this node. +* **weak** - The transistors in this production rule are weak and can be +overridden by any other non-weak production rule. Use this for keepers. +* **force** - The transistors in this production rule are very strong and +cannot be overridden at all. This also overrides normal production rules. Use +this for power rails. +* **pass** - The production rule allows bi-directional current flow from source +to drain or from drain to source. +* **after=0** - Specify a maximum delay for this production rule where `0` can +be any integer representing some number of pico-seconds. + +## Timing Assumptions + +Timing assumptions may be optionally specified using the following syntax: +`G0 -> S {G1}`. This will block the production rule from firing as long as G1 +evaluates to GND. Instabilities on `G1` do not propagate out into the rest of +the circuit. + +## Examples + +With weak feedback, shared gate networks, +and anonymous nodes, and sizing, the above turns into this: + +``` +require driven, stable, noninterfering +@_3&R.r<1>|_Reset<3>&L.r<3>&R.e<3>->v1- +~v1<1>->R.r+ +R.r<1>->L.e- +@_4&~R.r<1>|~_Reset<1>|~L.r<2>&~R.e<2>->v1+ +v1<1>->R.r- +~R.r<1>->L.e+ + +L.e'1<1>->v0- +~v0<1>->L.r'1+ +~L.e'1<1>->v0+ +v0<1>->L.r'1- + +R.r'1<1>->R.e'1- +~R.r'1<1>->R.e'1+ + +Vdd<0.1>->_3- [weak] +~GND<0.1>->_4+ [weak] +```