Git Product home page Git Product logo

tydi's People

Contributors

bors[bot] avatar dependabot-preview[bot] avatar dependabot[bot] avatar johanpel avatar jvanstraten avatar mbrobbel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tydi's Issues

Requirements when C<5, Dimensionality = 0 and Throughput > 1

Background

https://abs-tudelft.github.io/tydi/specification/physical.html#signal-omission

Specifies that:

  • stai is contingent on C≥6∧N>1
  • endi is contingent on (C≥5∨D≥1)∧N>1
  • strb is contingent on C≥7∨D≥1

Potential Issue

As a result of these requirements, a Stream with C<5, D=0 and element lanes N>1 should not have an endi signal.
This means that all lanes in a transfer must be valid, as there is no way to set individual lanes as being inactive.

Suggested Fix

The current specification implies that any transfer with C<5, D=0, N>1 must consist of Q elements where Q mod N = 0.

It is not clear whether this is intentional, I can think of two other ways in which to make behavior consistent:

  1. N>1 is contingent on D>0∨C≥5 - i.e., it is not allowed to transfer multiple elements at the same time except in a list or at higher complexities.
  2. endi is solely contingent on N>1 - i.e., it is always allowed to transfer multiple elements, and endi can be used to encode lane validity when sequences do not align to the number of element lanes.

A Tydi language

This is a tracking issue for the first steps towards implementing a hardware description language for Tydi types.

Currently the Tydi crate provides modules with the logical and physical stream type definitions and methods as described in the specification. There is also a design module that defines streamlets, a parser module for streamlet definition files and a generator module that can generate HDL templates based on streamlet definition files. The tydi command line application exposes these capabilities.

In order to provide an ergonomic solution for #51, this issue tracks the requires steps to move from an (embedded) library based approach to a high-level language approach. This issue attempts to provide tasks and steps for the initial phase of the language, compiler and tooling design.

Motivation

The benefits from adapting Tydi typed streams (🐬) in hardware designs may be snowed under the added effort and struggle (🐠) for hardware developers to make their designs compatible with Tydi streams.

Goals

The goals during this phase

for the Tydi language:

  • Define generic Tydi stream types and streamlets using these types
  • Provide a Tydi standard library with common (stream) type definitions
  • Support easy generation to enable mapping other high-level data types to Tydi stream types

for the tooling:

  • Re-usability of Tydi stream types and streamlet definitions across projects
  • Batteries-included tooling for Tydi project management and compilation
  • IDE support with syntax highlighting and compiler diagnostics

Non-goals

During this phase the Tydi language does not support:

  • Definition of streamlet behavior
  • Streamlet composition to build larger designs of connected streamlets

Design

Syntax

Tydi's syntax is heavily inspired by Rust's syntax.

Tokens

  • Input is interpreted as a sequence of UTF-8 code points
  • Tydi is a free-form language
  • Whitespace is any unicode code point with the white space character property set
  • Source file extension is .td.

Keywords

# constant generic
const

# streamlet interface mode
in
out

# streamlet definition
streamlet

# type definition
type

# use statement
use

# type constructs
group
union
stream

# stream synchronization modes
sync
desync
flatten
flatdesync

# stream direction
forward
reverse

# boolean literals
true
false

Punctuation

(	# open paren
)	# close paren
[	# open square
]	# close square
{	# open curly
}	# close curly
<	# open angle
>	# close angle
:	# colon
;	# semicolon
::	# path separator
,	# comma
=	# equals
!	# exclamation

Literals

# decimal number
[0-9]+

# hexadecimal number
0x[0-9a-fA-F]+

# identifier
[a-zA-Z][a-zA-Z_0-9]*

Grammar

module = { item } ;
item = use_statement | type_definition | streamlet_definition ;

use_statement = "use" , identifier , { "::" , identifier } , ";" ;
type_definition = "type" , identifier , [ generic_definition ] , "=" , type , ";" ;
streamlet_definition = "streamlet" , identifier , [ generic_definition ] , (
                            "(" , interface , { "," , interface } , ")"
                          | "{" , streamlet_interface , { "," , streamlet_interface } , "}"
                        ) ;
streamlet_interface = identifier , ":" , interface ;
interface = mode , type ;
mode = "in"
     | "out" ;

type = stream
     | union
     | group
     | path ;
stream = "stream!" , "(" , type , type , direction , synchronicity , number , ")" ;
direction = "forward"
          | "reverse" ;
synchronicity = "sync"
              | "desync"
              | "flatten"
              | "flatdesync" ;
union = "union" , ( "(" , variants , ")" | "{" , fields , "}" ) ;
group = "group" , ( "(" , variants , ")" | "{" , fields , "}" ) ;
path = identifier , [ generic_args ] ;

variants = type , { "," , type } ;
fields = identifier , ":", type , { "," , identifier , ":" , type } ;

generic_definition = "<" , generic_param , { "," , generic_param } , ">" ;
generic_args = "<" , generic_arg , { "," , generic_arg } , ">" ;
generic_arg = type
            | "(" , expr , ")" ;
generic_param = [ "const" ] , identifier ;

expr = expr , "+" , expr
     | expr , "*" , expr
     | "(", expr , ")"
     | identifier
     | number ;

identifier = letter, { letter | digit | "_" } ;

hex = "0x" , hex_symbol , { hex_symbol } ;
hex_symbol = digit | ? [a-fA-F] ? ;
number = digit , { digit } ;

letter = ? [a-zA-Z] ?;
digit = ? [0-9] ?;

Documented example of Tydi source file using the proposed grammar:

// A line comment.

// A use statement to bring Tydi types in scope for use in this module.
// This is like Rust's use statement, without support for combining multiple
// leafs with a single root in a tree-like syntax.
use axi::AXI4;
use axi::lite::AXI4_Lite;

// A type definition statement to define a new `Bit` type. The right hand side
// uses the built-in constructor for the primitive `Bits` type. When using the
// std, these constructors can be replaced with its corresponding type
// definition, i.e. `std::Bits`.
// Please note, this is not a type alias, or synonym. The compiler considers
// `Bit` and `bits!(1)` two different types. They are however compatible
// according to the Tydi specification. This allows for typesafe wrappers for
// example for signed/unsigned integer types.
type Bit = bits!(1);

// Type definitions can be generic. This example shows a constant generic for
// a `Bits` type. This allows the usage of a const expression in other type
// definitions, as shown for example in the `Bytes` type definition.
type Bits<const N> = bits!(N);
type Byte = Bits<(8)>;
type Bytes<const N> = Bits<( 8 * N )>;

// For product and sum types the `group` and `union` keywords are used. Tydi
// supports both unnamed variants (`Foo`) and named fields (`Bar`) for both
// groups and unions.
type Foo = group(Bit, Bit);
type Bar = group {
    foo: Bit,
    bar: Bit
};
type FooBar = union(Foo, Bar);
type BarFoo = union {
    bar: Bar,
    foo: Foo
};

// For streams the built-in `stream!` constructor can be used. The arguments of
// this constructor are (`element_type`, `user_type`, `direction`,
// `synchronicity`, `dimensionality`). The keywords as listed above are valid
// on corresponding locations here. Please note that, like the `bits`
// constructor, the `stream` constructor is used most often in the std. With
// additional generic argument type support these constructors can be exposed
// as generic type definitions (const generics already allow for the `Bits`
// definition). In the future there may be a type definition in the std for
// `Stream<..?>`.
// Below are the some type defintions from the Tydi paper, which also shows how
// type definitions can be generic over types.
type Dim<T> = stream!(T, Null, forward, sync, 1);
type New<T> = stream!(T, Null, forward, sync, 0);
type Des<T> = stream!(T, Null, forward, desync, 0);
type Flat<T> = stream!(T, Null, forward, flatten, 0);
type Rev<T> = stream!(T, Null, reverse, sync, 0);

// Now using these type definitions, more type definitions can be written.
// These examples are also from the Tydi paper.
type List<T> = Dim<T>;
type Vec<T, const N> = group(Bits<(N)>, New<T>);

// Streamlet definitions define a streamlet with its interface types. Streamlet
// definitions can be generic and the interface names can be unnamed or named.
type Sum<const N> = Bits<(N)>;
type Carry = Bit;

streamlet HalfAdder<const N>(
    in Bits<(N)>,
    in Bits<(N)>,
    out Sum<(N)>,
    out Carry
)

streamlet Adder<const N> {
    input: in group {
        a: Bits<(N)>,
        b: Bits<(N)>,
        carry: Carry,
    },
    output: out group {
        sum: Sum<(N)>,
        carry: Carry
    }
}

// Streamlets support stream types on their interfaces.
type Char = Byte;
type String = List<Char>;

streamlet WordCounter<const N, const M> {
    words: in String,
    counts: out List<
        group {
            words_counted: Bits<(N)>,
            bytes_read: Bits<(M)>,
        }
    >,
}

// And one more example e.g. `sha.td`:
use std::List;

streamlet SHA256(in List<Bits<(512)>>, out Bits<(256)>)

The grammar listed here is a proposal and open for discussion. There are already several issues to be discussed:

  • Const generic expressions require parentheses for LL(1) parsing
  • Const generics can't be typed, there must a way to add type information e.g. const N: i32
  • Built-in constructors are special, which may be unfavourable

Semantics

todo

  • Supported const generics types const N: ??

Compiler

The Tydi compiler takes Tydi source files, parses them into an abstract syntax tree, resolves names, checks types and generates an in-memory high-level intermediate representation (hir) that can be used by backends for compiler output. References to sources are tracked outside the hir to allow us to provide a library for construction of these structures for generation tools (similair to the current approach).

In the initial phase of the project (type and streamlet definitions only) the compiler output consists of purely structural HDL templates (and helper methods) or design visualizations.

Project

A Tydi project consists of a manifest (tydi.toml) with project meta information and source files with type and streamlet defintitions.

Manifest

tydi.toml is a project's manifest file that contains all metadata and configuration information about the project.

The Tydi manifest file is inspired by Rust's Cargo.toml.

Example

This example tydi.toml shows all valid fields of a project manifest.

[project]                                      # Project metadata
name = "std"                                   # The project name
authors = ["Delft University of Technology"]   # List of project authors
description = "The Tydi standard library"      # Optional description of the project

[dependencies]                                 # Dependencies configuration
axi = { path = "/axi" }                        # Example of a path dependency
wishbone = { git = "[email protected]:...", ... } # Example of a git dependency

Structure

The files in a Tydi project are organised with the project manifest in the root directory of the project, and all module files in a src directory. The target directory is used for compiler output. This setup is inspired by Rust's cargo behavior.

> project_name
  > tydi.toml     # project file
  > src           # source directory
    > lib.td      # root module file (project_name)
    > flow        # flow module directory
      > mod.td    # flow root module file (project_name::flow)
      > a.td      # a module file part of the flow module (project_name::flow::a)
    > b.td        # b module file (project_name::b)
  > target        # output directory
    > deps        # dependency cache
    > ...

Example std.td

// tydi std

// primitive (built-in)
type Bits<const N> = bits!(N);

// stream primitives
type Dim<T> = stream!(T, Null, forward, sync, 1);
type New<T> = stream!(T, Null, forward, sync, 0);
type Des<T> = stream!(T, Null, forward, desync, 0);
type Flat<T> = stream!(T, Null, forward, flatten, 0);
type Rev<T> = stream!(T, Null, reverse, sync, 0);

// extended primitives
type Null = Bits<(0)>;
type Bit = Bits<(1)>;
type Byte = Bits<(8)>;
type Bytes<const N> = Bits<( 8 * N )>;

// integers
type int<const N> = Bits<(N)>;
type uint<const N> = Bits<(N)>;

// containers
type List<T> = Dim<T>;
type Vec<T, const N> = group(Bits<(N)>, New<T>);
// todo: char encoding generic?
type String = List<Byte>;

Tasks

  • Design
    • Syntax and grammar
    • Generic parameter type system
    • Other built-in methods e.g. log2!
  • Compiler
    • Syntax
      • Lexer
      • Parser
    • Ast
    • Hir
    • Query system
  • Tooling
    • Update tydi cli
    • IDE support
      • Language server implementation
      • Syntax highlighting definition files
    • Project management tool
  • std

Future work

  • Streamlet behavior: impl Adder { ... }
  • Streamlet composition
    • Type casts for compatible types e.g. type Foo = Bit; type Bar = Bit; -> Foo as Bar
  • Interop with other languages
  • LLHD backend

Add concrete type definitions

Currently, Streamlet Definition Files support only the declaration of multiple streamlets as such:

Streamlet foo (
  x : in Stream<Bits<2>>,
  y : out Stream<Bits<2>>
)

Streamlet bar ...

In the internals of the generator tooling, one file is parsed to one library, containing the declared streamlets.

It would be nice to be able to define types, and use them across multiple libraries that reside in a single project.
Since the files do not only contain streamlet declarations, it would be nice to rename the files to .tydi file, and then be able to type something like the following.

For example, in some file: mills.tydi

type Wheat = Stream<Bits<1>>;
type Flour = Stream<Bits<4>>;

Streamlet windmill (
  wheat : in Wheat,
  flour : out Flour
)

And another file: bakery.tydi

use mills::Flour;

type Cookie = Stream<Bits<1>>;

Streamlet bakery (
  flour : in Flour,
  cookie : out Cookie
)

This means the type of the interfaces windmill.flour and bakery.flour are equal (their in/out mode is not part of the type), and can be connected if their mode corresponds accordingly.

However windmill.wheat and bakery.cookie do not have the same types. In other words, the windmill outputs the exact same type of Flour that the bakery can use as input, but Wheat is not a Cookie.

Flattening when both streams have user and/or keep properties

Background

When a Stream contains another Stream as its data, the Split function assigns both the parent and child streams "∅" (empty name), and employs "flattening" to combine their throughput, synchronicity, dimensionality and direction.

When a Stream has no element-manipulating data (data is either Null or a Stream) and no user property, it is discarded from the result. In effect, this creates a new physical stream with the original child Stream's data, combined with of the parent Stream's properties.

Issue

When keep (x) is true and/or user (T_u) is non-Null, the parent Stream must be retained.

If a parent Stream has keep=true and/or a non-Null user property, both Streams are still assigned "∅", but the parent Stream will conflict with the child Stream. Implementing the result of the Split function as a map in code, this means that either the child Stream simply replaces the parent Stream altogether (thereby losing the parent Stream's user property), or the Split function fails.

However, this behavior is not described in the specification, it only specifies that the names resulting from the Split function "are case-insensitively unique, emptyable strings consisting of letters, numbers, and/or underscores, not starting or ending in an underscore, and not starting with a digit" (emphasis mine).

Assumed/Suggested Fix

Right now, on an implementation level: The Split function should fail when it encounters a situation where two physical Streams have identical names.

On a specification level: It should be illegal for nested Streams (Streams which only have another Stream type as their data) to have a keep and/or user property on more than one of these Streams, and "flattening" should incorporate the singular user property into the resulting physical stream.

Alternatively, Streams should have a non-empty name property, as this will avoid conflicts in the result of the Split function.

Inconsistency: Strobe only encodes individual lane validity at C≥8

Issue

https://abs-tudelft.github.io/tydi/specification/physical.html#signals

Mentions that strb encoding individual lane validity is contingent on C≥8. This is repeated in a few other places.

However, https://abs-tudelft.github.io/tydi/specification/physical.html#signal-omission makes strb contingent on C≥7.
And https://abs-tudelft.github.io/tydi/specification/physical.html#complexity-c likewise suggests that "The indices of the active data lanes can be described with a simple range." only applies to C<7.

Assumed/Suggested Fix

strb encoding individual lane validity/activity should be contingent on C≥7. As otherwise, C=7 is identical in functionality to C=6.
Instead, the intent was most likely to have C≥7 add support for individual lane validity (a strb bit per element lane), while C≥8 adds support for individual lane-based sequence terminations (a last signal per element lane).

Can't compile due to type mismatch

The lexical-core package can't be compiled due to the standard library defining the ::BITS constant: rust-lang/rust#81654

E.g.:

error[E0308]: mismatched types
    --> [...]/lexical-core-0.6.7/src/atof/algorithm/math.rs:1049:42
     |
1049 |     let mut count = index.saturating_mul(Limb::BITS);
     |                                          ^^^^^^^^^^ expected `usize`, found `u32`

This prevents Tydi from being compiled, but can be resolved by updating the lexical-core package (performing an update of all packages does not appear to have an adverse effect, either).

Improve generator code path

The current general flow of the generator tool got a bit messy and is as follows:

                CLI Options
                    |
                    V
    +------------- CLI 
    |                  
SDF files              
    |                  
    V                  
 Parser -> Tydi -> Physical -> Common (canon) -> Back-end -> Source files
             |                                      ^
             |                                      |
             +---------------> Common (user) -------+

It is desired to change the flow to something where the common library is just a helper module for the various back-ends.
The entry point of a back-end is a Tydi generator project.

        CLI Options
            |
            V
    +----- CLI -------+
    |                 |
SDF files       Abstraction lvl.
    |                 |
    V                 v
 Parser -> Tydi -> Back-end -> Source files

Requirements for "last" signalling conflict for Streams with lower complexities and multiple element lanes

Background

Using https://abs-tudelft.github.io/tydi/specification/physical.html#signals

For N lanes, indexed 0 through N-1:
C < 8: All last bits for lanes 0 to N−2 inclusive must be driven low by the source, and may be ignored by the sink.
C < 8: All strb bits must be driven to the same value by the source. The sink only needs to interpret one of the bits. (strb effectively doesn't exist, except to indicate empty sequences)
C < 6: stai must always be driven to 0 by the source, and may be ignored by the sink. (Elements are aligned to lane 0.)
C < 5: endi must be driven to N−1 by the source when last is zero, and may be ignored by the sink in this case. (Effectively, all lanes must be used, except when transferring the end of a sequence.)
C < 4: It is illegal to assert the last bit for dimension 0 when the respective data lane is inactive, except for empty sequences. (Effectively, last may not be postponed.)

Issue

Taking a sequence of 7 elements with dimensionality D = 1 over a physical stream with 4 element lanes and complexity C < 4:

Transfer 1:
[active, active, active, active
All lanes must be used, because stai must be 0, and endi must be N-1. (Because C < 5 and C < 6)

Transfer 2:
active, active, active], inactive ?
The data must be aligned to lane 0 (C < 6: stai must be 0)
The last of dimension 0 must be asserted on the third lane. (C < 4: It is illegal to assert the last bit for dimension 0 when the data of that lane is inactive.)
But also, the last of any dimension must actually be asserted on the fourth lane? (C < 8: last of lanes 0 through N-2 must be driven low. strb effectively doesn’t exist.)

Assumed/Suggested Fix

The key inconsistency is derived from:

C < 4 It is illegal to assert the last bit for dimension 0 when the respective data lane is inactive, except for empty sequences.

At C < 8, the last signal does not refer to specific data lanes, but operates on a per-transfer level. The intent of this rule is to prevent postponing the last signal, not to establish requirements for data lanes with respect to the last signal. Hence, the rule should probably be changed to say:

C < 4 It is illegal to assert the last bit for dimension 0 when the transfer data is inactive, except for empty sequences.

or

C < 4 It is illegal to assert any last bit when the transfer data is inactive, except for empty sequences.

Logical stream iterators

Here are some suggestions to improve the API for logical streams:

/// Rename from LogicalStreamType
pub enum LogicalType {
  Null,
  Bits(Positive),
  Group(Group),
  Union(Union),
  Stream(Stream),
}

impl LogicalType {
  /// Returns an iterator over resulting split items. 
  pub fn split(&self) -> Split { ... }
  /// Returns an iterator over the physical streams resulting from splitting 
  /// and mapping the element streams to physical streams.
  pub fn physical(&self) -> PhysicalSplit { ... }
}

/// An iterator over split items from a LogicalType.
pub struct Split(indexmap::set::IntoIter<SplitItem>);

impl Iterator for Split {
  type Item = SplitItem;
}
impl DoubleEndedIterator for Split { ... }
impl ExactSizeIterator for Split { ... }

/// An iterator over physical split items from a LogicalType.
pub struct PhysicalSplit(indexmap::set::IntoIter<SplitItem>);

impl Iterator for PhysicalSplit {
  type Item = PhysicalSplitItem;
}
impl DoubleEndedIterator for PhysicalSplit { ... }
impl ExactSizeIterator for PhysicalSplit { ... }

/// An element stream with a path name and LogicalType. Contains no nested 
/// streams.
pub struct ElementStream {
  path_name: PathName,
  logical_type: LogicalType,
}

impl ElementStream {
  /// Returns the LogicalType of this element. Contains no nested streams.
  pub fn logical_type(&self) -> &LogicalType {
    &self.logical_type
  }
  /// Return all fields in this element stream
  pub fn fields(&self) -> Fields { ... }
}

impl From<ElementStream> for PhysicalStream { ... }

pub struct Signals(LogicalType);
impl Signals {
  /// Returns the LogicalType of this element.
  pub fn logical_type(&self) -> &LogicalType {
    &self.0
  }
  /// Returns all fields in these async signals.
  pub fn fields(&self) -> Fields { ... }
}

/// A split item is either an async signal (outside streamspace) or an element
/// stream (no nested streams).
pub enum SplitItem {
  Signals(Signals),
  Stream(ElementStream),
}

/// A split item is either an async signal (outside streamspace) or a physical
/// stream.
pub enum PhysicalSplitItem {
  Signals(Signal),
  Stream(PhysicalStream),
}

For the uses case of @ahadnagy this would result in something like this:

let haystack: LogicalType = ...;
let needle: &[Name] = ...;

let search: Option<SplitItem> = haystack
            .split()
            .find(|split_item|
                match split_item {
                    SplitItem::Signals(signals) => signals.fields(),
                    SplitItem::Stream(element_stream) => element_stream.fields(),
                }
                .keys()
                .windows(needle.len())
                .any(|name| name == filter)
            );

Given that we modify the Fields::keys method to return &[Name] instead of &PathName.

This also solves #27. We could also use a marker trait to distinguish between nested and unnested LogicalTypes.

Union semantics and variant enumeration order for tag bits

Currently, the spec says:

https://abs-tudelft.github.io/tydi/specification/logical.html#union-semantics

The "tag" field is used to represent which variant is being represented, by means of a zero-based index encoded as an unsigned binary number.

Do we want to allow custom tag enumeration of union variants?
If yes, we should add this to the spec.
If no, we should make explicit that the current text refers to the field index in the union declaration.
In the example, the tag enumeration is done by order of variant appearance in the union declaration, which seems most logical and straightforward, and it is similar to what most programming languages do as far as I am aware.

Transferring empty outer lists at lower complexities

Background

https://abs-tudelft.github.io/tydi/specification/physical.html#last-signal-description

[C<4] It is illegal to assert a last bit for dimension j without also asserting the last bits for dimensions j′<j in the same lane.

[C<4] It is illegal to assert the last bit for dimension 0 when the respective data lane is inactive, except for empty sequences.

The first rule suggests that at C<4, and (as an example) D=3, it is not possible to assert last="100" or last="110", or last="010").

Issue

This means that at C<4, it is illegal to transfer empty outer lists, which in turn means that Stream complexity has an effect on the kinds of data transferred.

For instance, the example given in the last signal description for C≥8, D=2
["Hello", "World"], ["Tydi", "is", "nice"], [""], []
makes use of an empty outer list (the last element, []). This requires asserting last="10".

Suggested Fix

It is not clear whether this is intentional. However, I am of the opinion that Stream properties should not affect the kind of data which can be transferred.

As such, I recommend amending the first rule in the Background section of this issue to also include an exception for empty sequences.

Tydi TODO

The following things have come up that are either broken or aren't very nice about the current specification.

  • Asynchronous signals cannot be reversed (#23). Proposed solutions:
    • Make reversal part of fields instead of Stream nodes. (This is problematic because at best it introduces implicit streams again. It also means tools cannot reasonably place registers/clock domain crossings automatically.)
    • Remove the signals from the specification, but add a separate type system (based on Tydi, bit supporting reversal) for user-defined signals.
    • Keep the current specification for signals that tools may insert registers into, but add a separate type system (based on Tydi) for user-defined signals that supports reversal that the tools don't do anything with.
  • Order of asynchronous signals and streams is not maintained.
  • The user-friendly representation notes should be removed. This and any requirements on field names beside uniqueness are entirely in user control. Code generators are considered to be users in this context; this means that generators may impose additional constraints.
  • Add a flag to Stream nodes and to physical streams that specifies whether empty sequences are supported or not.
  • Maybe add a note that Tuple(N, T) may be used as a shorthand for Group(0: T, 1: T, ..., N-1: T).

Add license

For the specification: CC-BY
For the specification: ?
For the implementation: Apache-2.0

  • Add license files to repository
  • Add note in README.md about licenses for this project

It is not clear whether "stai" and "endi" signals are significant when "strb" is low.

Issue

While https://abs-tudelft.github.io/tydi/specification/physical.html#data-signal-description indicates that stai and endi are redundant when C≥7 (which adds support for a per-lane strb (strobe) signal), it is not clear whether stai and endi are significant when strb is low, although this is implied.

At lower complexities (C<7) the strb signal still exists, but only as a single bit to indicate that the entire transfer's data is inactive.

For a concrete example, it is unclear what values stai and endi should have when transferring an "empty sequence".

Assumed/Suggested Fix

stai and endi are insignificant when the strb of that index's data lane is driven low. (Which at C < 7, means that stai and endi are insignificant altogether when strb='0'.)

Note/Potential Issue

Another potential issue might arise from a lack of clarity around stai and endi when C≥7. While the above data signal specification indicates that

while a source that desires individual control over the lanes and thus has C≥7 would probably always drive endi to N−1 and stai to 0, a sink with complexity C≥7 still needs to accept input from sources that do use endi and stai.

This does not clarify whether a source with C≥7 should employ endi and stai in such a way.

In the event that a source transfers
stai: 0, endi: 0, strb: 11010
do the stai and endi signals take precedence? (I.e., only lane 0 is truly active.)

Now, what about:
stai: 0, endi: 0, strb: 01010
Since lane 0 is now inactive, taking my suggested fix, stai and endi are insignificant and lanes 1 and 3 are active.

Hence, I propose specifying that
"stai and endi are only significant when all strb bits are driven high"
As this still allows for sources with C<7 to be compatible with sinks with C≥7, but does not allow for sources with C≥7 to create confusing transfers.

Non-stream signal reversibility

Currently, it is allowed to specify a reverse signal on the Stream node.
It would be nice to also allow reverse signals on signals that are not streams.

It seems more logical to me that reverse should be a property of a group and union field, as "reverse" is always relative to something else.

Since at the type level, interface modes are (obviously) not exposed, it must be relative to something else within a Logical"Stream"Type, and thus it can only be another group or union field.

If such a group or union element with a reversed field is streamed, the "synthesis" algorithms probably need to be adjusted to create new streams for those specific fields.

Example of a Streamlet Definition File with:

Streamlet dolphin {
  x : out Stream<Group<a: rev Bits<1>, b: Bits<1>>>
}

Would result in two streams: x.a going out and x.b coming in, both having the same properties of the parent stream.

For unions, if the first field is reversed, e.g.:

Streamlet triggerfish {
  y : out Stream<Union<a: rev Bits<1>, b: Bits<1>>>
}

Not entirely sure but I think this should result in two streams:

y.(tag & b) going out and may not assume y.a. will send something before sending the tag.
y.a coming in.

Double underscores are illegal in VHDL

The (current) decision is to use a single underscore for hierarchy and make underscores illegal in field identifiers. The spec needs to be updated to reflect this at some point.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.