diff --git a/philosophy/combinatorial_dependency.md b/philosophy/combinatorial_dependency.md index 85f32cc..56929a8 100644 --- a/philosophy/combinatorial_dependency.md +++ b/philosophy/combinatorial_dependency.md @@ -1,2 +1,9 @@ # Combinatorial Dependency +Still a vague idea. + +Ports in interfaces are combinatorially dependent on each other. To break combinatorial dependency, one has to use multiple interfaces. + +`module m : int a, int b -> int out` + +`out` is combinatorially dependent on `a` and `b` diff --git a/philosophy/compiletime_and_runtime.md b/philosophy/compiletime_and_runtime.md new file mode 100644 index 0000000..0b87cd1 --- /dev/null +++ b/philosophy/compiletime_and_runtime.md @@ -0,0 +1,30 @@ +# Separating Compiletime and Runtime + +Requirements: +- Code that describes plain hardware should be minimal to write. One shouldn't have to 'break out' of the generative environment to write plain hardware code. +- It should be easy to write generative code mixed with plain hardware. + +## Differences +### Compile Time +Arrays need not be bounded. Integers need not be bounded. + +### Runtime +Arrays that have dynamic indices must have a fixed size. +Integers must be bounded. + +## Multiplexer inference +There is quite a significant difference between an array access with a constant, and one which should infer a multiplexer, but in both cases the syntax in other languages is exactly the same: `my_arr[idx]` + +The constant index should infer to just a wire connection which costs nothing. In this case the different wires that are part of an array don't have any relation to each other in hardware. This allows us to bestow other properties as well. For example constant indices don't conflict with each other if they don't point to the same element. Runtime indices do. Array wires with constant indices don't enforce any latency requirements upon each other. 'dynamically sized' arrays can only be indexed with compile time indices. Etc. + +With a runtime index (based on an integer wire in the design) should infer to a multiplexer. And then of course the array wires do have a relation. + +An initial thought to distinguish the two was to just check for constant-ness of the array argument, which can be done at flattening time. But that wouldn't make the distinction clear enough. + +Proposal: Require `mux` keyword for any runtime array index which should infer to a multiplexer. + +Examples: +- `a[5] = 1;` constant index write +- `a[mux b] = 1;` multiplexed write +- `x = a[5];` constant index read +- `x = a[mux b];` multiplexed index write diff --git a/philosophy/images/fifoExample.drawio b/philosophy/images/fifoExample.drawio new file mode 100644 index 0000000..b214455 --- /dev/null +++ b/philosophy/images/fifoExample.drawio @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/philosophy/images/fifoExample.png b/philosophy/images/fifoExample.png new file mode 100644 index 0000000..863f8a5 Binary files /dev/null and b/philosophy/images/fifoExample.png differ diff --git a/philosophy/images/negativeBackedgeConcept.drawio b/philosophy/images/negativeBackedgeConcept.drawio index d11cf35..f7febe9 100644 --- a/philosophy/images/negativeBackedgeConcept.drawio +++ b/philosophy/images/negativeBackedgeConcept.drawio @@ -1,49 +1,49 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/philosophy/images/negativeBackedgeConcept.png b/philosophy/images/negativeBackedgeConcept.png index a8bf33a..5978b32 100644 Binary files a/philosophy/images/negativeBackedgeConcept.png and b/philosophy/images/negativeBackedgeConcept.png differ diff --git a/philosophy/latency.md b/philosophy/latency.md index 54588dd..f77ae8f 100644 --- a/philosophy/latency.md +++ b/philosophy/latency.md @@ -41,6 +41,9 @@ Usually it negative backedges would be found on the output of a module, which is ![Negative Backedge Concept](images/negativeBackedgeConcept.png) +As a more concrete example, consider the write side of a FIFO. +![FIFO Negative Backedge](images/fifoExample.png) + ## Requirements for the latency counting system - Addition or Removal of any latency registers that do not violate a constraint must not affect the operation of the design. - Feedback loops containing only latency are considered combinatorial feedback loops, and are therefore forbidden. Feedback loops must therefore have at least one state register in them. @@ -96,6 +99,14 @@ The issue starts when the inputs and outputs don't have predefined absolute late One may think the solution would simply be to prefer inputs over outputs or something, just to get a deterministic latency assignment. Just move b to be the earliest of the available latencies, but even in this case, if we instead looked at the possibilities of a, and fixed b, we would again make b later by making a earlier. And since there's no way to distinguish meaningfully between inputs, there's no deterministic solution either. -To this problem I only really see two solutions: -- Still perform full latency computation when compiling each module separately. In the case of non-deterministic latency assignment, reject the code and require the programmer to add explicit latency annotations. The benefit is better encapsulation, the programmer requires only the module itself to know what latencies are. The downside is of course less flexible modules. -- Perform latency computation at integration level, +To this problem I only really see three options: +- Still perform full latency computation when compiling each module separately. In the case of non-deterministic latency assignment, reject the code and require the programmer to add explicit latency annotations. The benefit is better encapsulation, the programmer requires only the module itself to know what latencies are. The downside is of course less flexible modules. Though is this flexibility _really_ needed? +- Infer absolute latencies on the inputs and outputs of submodules using templates which can be inferred. This would be really handy to allow latency information to flow back into the templating system, thus allowing a FIFO that alters its almostFull threshold based on its input latency. Of course, this makes absolute latency information flow from top-down instead of bottom up, so now getting the latency information back from the module would be impossible. The issue is that templates can't be instantiated partially. Either the submodule takes all of its port latencies from the calling module, or it determines its latencies itself. +- Perform latency computation at integration level, we don't define the absolute latencies on the ports of a module, unless the programmer explicitly does so. For simlpicity, this requires that every single module instantiation now compiles to its own Verilog module though, which is less than ideal for debugging. + +### Latency Graph Cycles are the key +So assigning absolute latencies is difficult, and no good solution can be found in isolated cases. Perhaps another approach would work better. + +In essense, what are the reasons for which we want to count out latencies? The initial one of course was keeping signals in sync. In the vast majority of cases when you pipeline a design, you don't want to cross signals from different time steps. But of course, after pipelining a design, you need to _deal_ with the effect that this module now takes several cycles, and has a certain capacity to store in progress data. + +Maybe instead of trying to infer the latencies from the pipeline with inputs and outputs, we focussed our attention purely on the cycles. These are already nice and constrained.