Introduction
Note: Ted is in a very early stage of design. The syntax and semantics described here are subject to change. We welcome feedback and contributions!
Ted (Timing-Explicit Description, aka The Teddy Bear Language) is a systems programming language with explicit logical time and deterministic concurrency. Hardware modeling is a first-class library built on the same semantics.
Philosophy
Ted is built on three core principles:
- Time as an effect -
@and event waiting are only legal in timed contexts; ordinary code stays direct and fast - Deterministic concurrency - Scheduling is defined in terms of logical time so runs are reproducible
- Systems performance - Core code compiles to tight machine code; timed code lowers to efficient state machines
A Quick Taste
mod blink {
out led: bit,
loop {
led = !led @ +500ms; // schedule the next toggle 500ms later
}
}
This is timed code: the task advances logical time and yields between updates.
Core vs Timed Ted
- Core Ted is ordinary systems code (functions, structs, loops, FFI). It does not involve a scheduler and never uses
@. - Timed Ted opts into explicit logical time. Timed contexts include
onhandlers andloopblocks inside modules today; futuretimed fnwill let timed code appear anywhere.
TODO: Define
timed fntasks and where they can appear outside modules.
Deterministic Concurrency
Scheduling is defined over logical time, so concurrent programs are deterministic by default. This enables reproducible tests and is designed to support replayable debugging and systematic exploration of schedules.
The @ Operator
Each timed task has a current logical time t:
on change(sig) {
// Schedule a future update
sig = 1 @ +10ns;
// Read the past (temporal values only)
let prev = sig @ -1;
}
@ is only legal in timed contexts. Ordinary values have no history; only temporal values support x @ -delta.
TODO: Add a standalone delay statement (
@ +delta;) for timed code that needs to advance time without assigning.
Temporal Storage
Not every value is time-travelable. Ports and module-level state are temporal, and future syntax will make temporal storage explicit (for example, a signal or temporal declaration). Temporal values keep bounded history so the compiler can allocate compact ring buffers and stay fast.
Hardware Is a Library
Hardware modeling is built on the timed core with libraries and conventions:
out led: bitis sugar for a temporal output typeon rising(clk)is shorthand for subscribing to a rising-edge event stream- Waveform dumping is a runtime option, not a semantic requirement
Next Steps
- Installation - Get Ted running on your machine
- Hello World - Write your first Ted program
- Time and
@- Deep dive into the timing model - Progress - Track implementation status and planned work
Progress
This page tracks implementation status and planned work. Update it alongside changes to the language semantics or tooling.
Status Snapshot
- Parser: stubbed; lexer and diagnostics are scaffolded
- Core syntax: documented; implementation pending
- Timed runtime: not implemented yet
- Cranelift backend: prototype emits a
mainthat only supports literalprint(...)calls - CLI: compile/check are wired; compile uses the prototype backend
Near-Term TODOs
- TODO: Implement the parser for module items, statements, and time operators.
- TODO: Define explicit temporal storage syntax and history bounds.
- TODO: Add a standalone delay statement (
@ +delta;) for timed code without assignment. - TODO: Specify
timed fntasks and where they can appear. - TODO: Define standard event sources (timeouts, intervals, channels) and ordering rules.
- TODO: Specify the core standard library surface (including I/O and
print). - TODO: Lower MIR into Cranelift and emit real program semantics.
Longer-Term Goals
- Lower timed code to explicit state machines and scheduled tasks.
- Build a deterministic scheduler with a fast event queue.
- Provide a standard library for timeouts, intervals, and waveform output.
Updating This Page
- Add links to issues/PRs once issue tracking is in place.
- Keep status short and factual; move detailed design notes into the language docs.
Installation
Prerequisites
Ted requires:
- Rust (stable, latest version recommended)
- Cargo (comes with Rust)
Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup update stable
Building from Source
Clone the repository:
git clone https://github.com/ted-lang/ted.git
cd ted
Build the compiler:
cargo build --release
The ted binary will be at target/release/ted.
Installation
Add to PATH
# Add to your shell profile (.bashrc, .zshrc, etc.)
export PATH="$PATH:/path/to/ted/target/release"
Or install via Cargo
cargo install --path crates/ted-cli
Verify Installation
ted --version
ted --help
Development Setup
For contributing to Ted:
# Install development tools
rustup component add rustfmt clippy
# Build and test
cargo build --workspace
cargo test --workspace
# Format and lint
cargo fmt --all
cargo clippy --workspace
Editor Support
Ted files use the .ted extension. Syntax highlighting is planned for:
- VS Code
- Vim/Neovim
- Emacs
Next Steps
Now that Ted is installed, continue to Hello World to write your first program.
Hello World
Let’s write your first Ted program. These examples use the timed hardware modeling library; core code looks like normal systems code and does not use @.
The Simplest Program
Create a file called hello.ted:
mod hello {
print("Hello from Ted!");
}
This prints a message to the terminal.
TODO:
Check the Program
ted check hello.ted
If there are no errors, you’ll see:
No errors found.
Compile (Prototype)
You can produce a native executable with the prototype Cranelift backend:
ted compile hello.ted --emit exe -o ./hello
./hello
You should see:
Hello from Ted!
TODO: Lower real program semantics into codegen; the current backend only supports literal
print(...)calls.
A Blinking LED
A more interesting example - an LED that toggles every 500ms:
mod blink {
out led: bit,
loop {
led = !led @ +500ms;
}
}
This demonstrates:
out led: bit- an output signalloop { ... }- continuous execution!led- toggle the current value@ +500ms- schedule the change 500ms in the future
A Counter
A classic 8-bit counter:
mod counter {
in clk: bit,
in reset: bit,
out count: u8,
on rising(clk) {
if reset {
count = 0;
} else {
count = count + 1;
}
}
}
This demonstrates:
- Multiple ports (
inandout) on rising(clk)- react to clock edges- Conditional logic with
if/else
Using Past Values
Detect a rising edge manually:
mod edge_detect {
in signal: bit,
out rising: bit,
on change(signal) {
rising = signal && !(signal @ -1);
}
}
The @ -1 reads the signal’s value from one cycle ago. This is only legal for temporal values like ports and module state.
Next Steps
- Syntax Overview - Learn the full syntax
- Time and
@- Master the timing model - Examples - More complete examples
Syntax Overview
Ted uses a Rust-like syntax that should feel familiar to systems programmers.
Basic Structure
A Ted program consists of modules (the current top-level unit). Modules can host both core code and timed event handlers:
mod my_module {
// port declarations
in input_signal: bit,
out output_signal: bit,
// logic
on change(input_signal) {
output_signal = input_signal;
}
}
Comments
// Single-line comment
/*
Multi-line
comment
*/
Identifiers
Identifiers follow Rust conventions:
- Start with a letter or underscore
- Contain letters, digits, or underscores
- Case-sensitive
signal_name
_private
counter123
Keywords
mod in out inout
on rising falling change
if else loop break
let fn return
bit u8 u16 u32 u64
i8 i16 i32 i64
Operators
Arithmetic
a + b // addition
a - b // subtraction
a * b // multiplication
a / b // division
a % b // modulo
Bitwise
a & b // AND
a | b // OR
a ^ b // XOR
~a // NOT
a << n // left shift
a >> n // right shift
Comparison
a == b // equal
a != b // not equal
a < b // less than
a <= b // less or equal
a > b // greater than
a >= b // greater or equal
Logical
a && b // logical AND
a || b // logical OR
!a // logical NOT
Calls
print("Hello from Ted!");
TODO: Function calls and string literals are prototype-level and will be specified alongside the core standard library.
Time
In timed contexts, @ is available for history reads and scheduled writes.
x @ -1 // history read (temporal values only, timed contexts)
x = 1 @ +10ns // schedule a write 10ns later
TODO: Add a standalone delay statement (
@ +delta;) for timed code that needs to advance time without assigning.
Blocks
Blocks use curly braces:
{
let x = 1;
let y = 2;
x + y
}
Statements vs Expressions
Most constructs are expressions that return values:
let result = if condition { a } else { b };
Time and @
Ted treats time as an explicit effect in timed contexts. Each timed task carries a current logical time t.
Timed Contexts
@ and event waiting are only legal in timed contexts, such as:
onhandlersloopblocks inside modules- (planned)
timed fntasks
Core code cannot use @ and compiles directly with no scheduler overhead.
TODO: Define
timed fnsyntax and formalize module-level timed tasks in the grammar and parser.
Logical Time Model
Timed code runs as tasks with a current logical time t. Scheduling is expressed with assignment offsets.
x = vwrites at the task’s current timetx = v @ +deltaschedules the write att+deltaand yields the taskx @ -deltareads a temporal value as it was att-delta
Here, delta is a non-negative time offset (cycles or time units) and can come from a literal or an expression.
Example:
on change(sig) {
sig = 1 @ +10ns;
}
TODO: Add a standalone delay statement (
@ +delta;) for timed code that needs to advance time without assigning.
Temporal Storage
Not every value is time-travelable. Only temporal values keep history:
- Ports and module-level state are temporal today
- Future syntax will make temporal storage explicit (for example,
signalortemporaldeclarations)
Temporal values keep bounded history so the compiler can allocate compact ring buffers. The history window can be inferred from @ -delta uses or declared explicitly.
TODO: Specify explicit temporal storage declarations (for example,
signalortemporal) and history bounds.
Reading the Past
Access previous values of a temporal signal:
Note: The examples in this section assume they appear inside a timed context (for example, an on handler or a module-level loop).
// Relative to current time
let prev = sig @ -1; // one cycle ago
let older = sig @ -5; // five cycles ago
// With time units
let past = sig @ -10ns; // 10 nanoseconds ago
let history = sig @ -1us; // 1 microsecond ago
Use Cases
Edge Detection:
let rising_edge = sig && !(sig @ -1);
let falling_edge = !sig && (sig @ -1);
Change Detection:
let changed = sig != (sig @ -1);
Delay Line:
on change(input) {
delayed = input @ -100ns;
}
Scheduling the Future
Schedule values for future logical time:
// Relative scheduling
sig = 1 @ +1; // schedule for the next cycle
sig = value @ +10; // 10 cycles from now
// With time units
sig = 1 @ +10ns; // in 10 nanoseconds
sig = 0 @ +1ms; // in 1 millisecond
Use Cases
Pulse Generation:
// Generate a 10ns pulse
pulse = 1;
pulse = 0 @ +10ns;
Delayed Response:
on rising(trigger) {
output = 1 @ +100ns;
}
Periodic Toggle:
loop {
led = !led @ +500ms;
}
Time Units
Ted supports these time units:
| Unit | Meaning |
|---|---|
| (none) | cycles |
ns | nanoseconds |
us | microseconds |
ms | milliseconds |
s | seconds |
These units are part of logical time; they do not imply wall-clock delays.
Combining Past and Future
You can use past values to determine future assignments:
// Capture now, then apply later
let prev = sig @ -1;
sig = !prev @ +1;
// Delayed feedback
let sample = input @ -1;
output = sample @ +10ns;
Rules and Constraints
- Timed only -
@is only legal in timed contexts - Temporal only - History reads (
x @ -delta) require temporal values - Scheduling -
x = v @ +deltaschedules a write and yields the task - No future reads -
x @ +deltais invalid as a read; only assignments can target the future - Evaluation timing -
x = v @ +deltaevaluatesvafter the time advance; capture values explicitly if needed - Grouping - Use parentheses for compound expressions (for example,
(a + b) @ +1) - Deterministic - Ordering is defined; same input produces identical output
- Progress - Timed loops must include time advancement or event waits to avoid zero-time nontermination
- Cycle-accurate - Integer offsets refer to logical cycles
- Time-accurate - Unit-based offsets refer to logical time units
Events
Timed Ted is event-driven. Event handlers execute in response to event sources (signals are one common source).
The on Keyword
The on keyword defines event handlers:
on <event> {
// code to execute
}
on introduces a timed context, so @ and event waits are legal inside the handler.
Handlers run at the current logical time; schedule assignments with @ +delta to advance time when needed.
TODO: Add a standalone delay statement (
@ +delta;) for timed code that needs to advance time without assigning.
Event Types
The following adapters are part of the hardware modeling library:
rising - Rising Edge
Triggers when a signal transitions from 0 to 1:
on rising(clk) {
count = count + 1;
}
falling - Falling Edge
Triggers when a signal transitions from 1 to 0:
on falling(clk) {
latch = data;
}
change - Any Change
Triggers on any value change:
on change(data) {
valid = 1;
output = process(data);
}
Timed events are not limited to hardware signals. The standard library will add event sources like timeouts, intervals, and channels so the same model applies to software and simulations.
TODO: Define the standard event source APIs (timeouts, intervals, channels) and their ordering rules.
Multiple Events
Multiple Handlers
You can have multiple handlers for the same signal:
on rising(clk) {
// happens first
}
on rising(clk) {
// happens second
}
Multiple Signals
React to changes in any of several signals:
on change(a, b, c) {
result = a + b + c;
}
Event Priority
When multiple events trigger simultaneously:
- Events are processed in declaration order
- All handlers for one signal complete before the next
- Nested events are queued, not immediately executed
Conditional Events
Use guards to filter events:
on rising(clk) if enable {
// only executes if enable is high
count = count + 1;
}
Comparison with Verilog
| Ted | Verilog |
|---|---|
on rising(clk) | always @(posedge clk) |
on falling(clk) | always @(negedge clk) |
on change(sig) | always @(sig) |
on change(a, b) | always @(a or b) |
These are convenience adapters; the core semantics are event sources plus timed handlers.
Best Practices
- Keep handlers small - Each handler should do one thing
- Avoid circular triggers - Don’t create infinite event loops
- Use rising/falling for clocks - More efficient than change
- Document timing assumptions - Especially for multi-signal handlers
Modules
Modules are the current top-level unit in Ted. Hardware modeling uses ports and event handlers, but core code can live inside modules without any timing.
Module Declaration
mod module_name {
// ports
// internal signals
// logic
}
Ports
Ports define the module’s interface for hardware modeling:
mod example {
in clock: bit, // input port
out data: u8, // output port
inout bus: u32, // bidirectional port
}
Port Directions
| Direction | Description |
|---|---|
in | Input - read only inside module |
out | Output - write only inside module |
inout | Bidirectional - read and write |
Port Types
Ports must have explicit types:
in single_bit: bit,
in byte_value: u8,
in wide_bus: u64,
Ports are convenience syntax for temporal values; they can be modeled as library types like Signal<Bit, Out>.
Internal Signals
Signals declared without direction are internal:
mod counter {
in clk: bit,
out count: u8,
// Internal signal
let overflow: bit = 0;
on rising(clk) {
if count == 255 {
overflow = 1;
}
count = count + 1;
}
}
Timed Contexts
on handlers and module-level loop blocks are timed contexts. @ is only legal inside them; helper code outside remains ordinary core Ted.
TODO: Add explicit timed task declarations and
timed fnfor timed code outside modules.
Module Instantiation
Instantiate modules within other modules:
mod top {
in clk: bit,
out led: bit,
// Instantiate a counter
counter my_counter {
clk: clk,
count: _, // unconnected
};
// Use counter output
led = my_counter.overflow;
}
TODO: Define the exact semantics of module-level assignments (for example, continuous assignment vs event-driven evaluation).
Connection Syntax
module_type instance_name {
port_name: signal,
another_port: another_signal,
};
Use _ for unconnected ports.
Parameterized Modules
Modules can have parameters:
mod counter<WIDTH: u32 = 8> {
in clk: bit,
out count: uint<WIDTH>,
on rising(clk) {
count = count + 1;
}
}
// Instantiate with custom width
counter<16> wide_counter { ... };
Module Hierarchy
Modules can contain other modules:
mod system {
in clk: bit,
// Connect them
let bus: u32;
cpu processor { clk: clk, data_out: bus };
memory ram { clk: clk, data_in: bus };
}
Visibility
By default, all ports are visible. Use _ prefix for internal implementation details:
mod example {
out result: u8, // Public
let _temp: u8, // Private (convention)
}
Types
Ted has a simple, systems-oriented type system with explicit widths. Temporal storage is explicit by design and opt-in.
Temporal Storage
Ted distinguishes ordinary values from temporal values. Only temporal values keep history and allow x @ -delta.
- Ports and module-level state are temporal today
- Future syntax will make temporal storage explicit (for example,
signalortemporaldeclarations)
Temporal values keep bounded history so the compiler can allocate compact ring buffers and avoid unbounded storage.
TODO: Define explicit temporal storage syntax (for example,
signalortemporal) and history bounds.
Primitive Types
Bit
Single binary value (0 or 1):
let flag: bit = 0;
let enable: bit = 1;
Unsigned Integers
Fixed-width unsigned integers:
| Type | Width | Range |
|---|---|---|
u8 | 8 bits | 0 to 255 |
u16 | 16 bits | 0 to 65,535 |
u32 | 32 bits | 0 to 4,294,967,295 |
u64 | 64 bits | 0 to 18,446,744,073,709,551,615 |
let byte: u8 = 42;
let word: u32 = 0xDEADBEEF;
Signed Integers
Fixed-width signed integers (two’s complement):
| Type | Width | Range |
|---|---|---|
i8 | 8 bits | -128 to 127 |
i16 | 16 bits | -32,768 to 32,767 |
i32 | 32 bits | -2^31 to 2^31-1 |
i64 | 64 bits | -2^63 to 2^63-1 |
let offset: i16 = -100;
Custom Width
For arbitrary bit widths:
let nibble: uint<4> = 0xF;
let wide: uint<128> = 0;
Arrays
Fixed-size arrays:
let memory: [u8; 256]; // 256 bytes
let registers: [u32; 16]; // 16 registers
// Access
let value = memory[0];
memory[index] = data;
Structs
Group related signals:
struct Packet {
valid: bit,
data: u64,
tag: u8,
}
let pkt: Packet;
pkt.valid = 1;
pkt.data = payload;
Type Inference
Types can often be inferred:
let x = 42; // inferred as u32
let y = x + 1; // also u32
let flag = true; // inferred as bit
Type Conversions
Explicit Casts
let narrow: u8 = wide as u8; // truncate
let wider: u32 = narrow as u32; // zero-extend
Bit Extraction
let byte = word[7:0]; // bits 7 down to 0
let nibble = byte[3:0]; // lower nibble
Concatenation
let combined = {high, low}; // concatenate
let word = {byte, byte, byte, byte};
Constants
Compile-time constants:
const WIDTH: u32 = 8;
const DEPTH: u32 = 256;
const INIT: u8 = 0xFF;
Literals
Integer Literals
42 // decimal
0xFF // hexadecimal
0b1010 // binary
0o77 // octal
1_000_000 // underscores for readability
Boolean Literals
true // same as 1 for bit
false // same as 0 for bit
Time Literals
10ns // nanoseconds
1us // microseconds
100ms // milliseconds
1s // seconds
String Literals
"Hello from Ted!"
TODO: Strings are prototype-only today and will be specified with the core standard library.
Grammar
This is the formal grammar for Ted in EBNF notation.
TODO: This grammar is evolving. It reflects the documented syntax but does not yet capture every planned feature (for example,
fnand advanced type forms).
Lexical Grammar
(* Whitespace and comments *)
whitespace = " " | "\t" | "\n" | "\r" ;
line_comment = "//" , { any_char - "\n" } , "\n" ;
block_comment = "/*" , { any_char } , "*/" ;
(* Identifiers *)
ident = ( letter | "_" ) , { letter | digit | "_" } ;
letter = "a" | ... | "z" | "A" | ... | "Z" ;
digit = "0" | ... | "9" ;
(* Literals *)
int_literal = decimal | hex | binary | octal ;
decimal = digit , { digit | "_" } ;
hex = "0x" , hex_digit , { hex_digit | "_" } ;
binary = "0b" , ( "0" | "1" ) , { "0" | "1" | "_" } ;
octal = "0o" , octal_digit , { octal_digit | "_" } ;
time_literal = int_literal , time_unit ;
time_unit = "ns" | "us" | "ms" | "s" ;
bool_literal = "true" | "false" ;
string_literal = "\"" , { ? any character except \" and newline ? | escape_sequence } , "\"" ;
escape_sequence = "\\\\" , ( "\"" | "\\" | "n" | "r" | "t" ) ;
(* Keywords *)
keyword = "mod" | "in" | "out" | "inout" | "let" | "const"
| "on" | "rising" | "falling" | "change"
| "if" | "else" | "loop" | "break" | "return"
| "fn" | "struct" | "true" | "false" ;
Syntax Grammar
(* Program structure *)
program = { module } ;
module = "mod" , ident , [ generic_params ] , "{" , module_body , "}" ;
module_body = { port_decl | const_decl | signal_decl | struct_decl | module_stmt | event_handler | timed_loop | module_inst } ;
(* Ports and signals *)
port_decl = port_dir , ident , ":" , type , "," ;
port_dir = "in" | "out" | "inout" ;
signal_decl = "let" , ident , [ ":" , type ] , [ "=" , expr ] , ";" ;
const_decl = "const" , ident , ":" , type , "=" , expr , ";" ;
struct_decl = "struct" , ident , "{" , { struct_field } , "}" ;
struct_field = ident , ":" , type , "," ;
(* Types *)
type = primitive_type | array_type | custom_type ;
primitive_type = "bit" | "u8" | "u16" | "u32" | "u64"
| "i8" | "i16" | "i32" | "i64"
| "uint" , "<" , expr , ">" ;
array_type = "[" , type , ";" , expr , "]" ;
custom_type = ident , [ generic_args ] ;
(* Events *)
event_handler = "on" , event_spec , [ "if" , expr ] , block ;
event_spec = event_type , "(" , ident_list , ")" ;
event_type = "rising" | "falling" | "change" ;
ident_list = ident , { "," , ident } ;
(* Timed tasks *)
timed_loop = "loop" , block ;
(* Expressions *)
expr = time_expr | binary_expr | unary_expr | postfix_expr ;
unary_expr = unary_op , expr ;
unary_op = "!" | "~" | "-" ;
binary_expr = expr , binary_op , expr ;
binary_op = "+" | "-" | "*" | "/" | "%"
| "&" | "|" | "^" | "<<" | ">>"
| "==" | "!=" | "<" | "<=" | ">" | ">="
| "&&" | "||" ;
time_expr = postfix_expr , "@" , time_offset ;
time_offset = ( "+" | "-" ) , expr ;
postfix_expr = primary_expr , { field_access | index_access | slice_access | call_suffix } ;
field_access = "." , ident ;
index_access = "[" , expr , "]" ;
slice_access = "[" , expr , ":" , expr , "]" ;
call_suffix = "(" , [ expr_list ] , ")" ;
expr_list = expr , { "," , expr } ;
primary_expr = ident | literal | "(" , expr , ")" | block | concat_expr ;
concat_expr = "{" , expr , { "," , expr } , "}" ;
literal = int_literal | bool_literal | time_literal | string_literal ;
(* Statements *)
stmt = let_stmt | assign_stmt | if_stmt | loop_stmt | break_stmt | expr_stmt ;
(* Module-level statements (combinational logic) *)
module_stmt = assign_stmt | expr_stmt ;
let_stmt = "let" , ident , [ ":" , type ] , [ "=" , expr ] , ";" ;
assign_stmt = lvalue , "=" , expr , ";" ;
lvalue = ident , { field_access | index_access | slice_access } ;
if_stmt = "if" , expr , block , [ "else" , ( block | if_stmt ) ] ;
loop_stmt = "loop" , block ;
break_stmt = "break" , ";" ;
expr_stmt = expr , ";" ;
block = "{" , { stmt } , [ expr ] , "}" ;
(* Module instantiation *)
module_inst = ident , [ generic_args ] , ident , "{" , connections , "}" , ";" ;
connections = { ident , ":" , ( expr | "_" ) , "," } ;
(* Generics *)
generic_params = "<" , generic_param , { "," , generic_param } , ">" ;
generic_param = ident , ":" , type , [ "=" , expr ] ;
generic_args = "<" , expr , { "," , expr } , ">" ;
Operator Precedence
From highest to lowest:
| Precedence | Operators | Associativity |
|---|---|---|
| 1 | @ (time) | Left |
| 2 | ! ~ - (unary) | Right |
| 3 | * / % | Left |
| 4 | + - | Left |
| 5 | << >> | Left |
| 6 | < <= > >= | Left |
| 7 | == != | Left |
| 8 | & | Left |
| 9 | ^ | Left |
| 10 | ` | ` |
| 11 | && | Left |
| 12 | ` | |
| 13 | = (assignment) | Right |
Example Programs
This section contains complete Ted example programs. These examples focus on the timed hardware modeling library and explicit time semantics.
Basic Examples
Hello World
The simplest Ted program:
mod hello {
print("Hello from Ted!");
}
TODO:
Blink
Toggle an LED every 500ms:
mod blink {
out led: bit,
loop {
led = !led @ +500ms;
}
}
Counter
8-bit counter with reset:
mod counter {
in clk: bit,
in reset: bit,
out count: u8,
on rising(clk) {
if reset {
count = 0;
} else {
count = count + 1;
}
}
}
Intermediate Examples
Edge Detector
Detect rising and falling edges:
mod edge_detector {
in signal: bit,
out rising: bit,
out falling: bit,
on change(signal) {
let prev = signal @ -1;
rising = signal && !prev;
falling = !signal && prev;
}
}
Pulse Generator
Generate a pulse of specific width:
mod pulse_gen {
in trigger: bit,
in width: u32, // pulse width in cycles
out pulse: bit,
on rising(trigger) {
pulse = 1;
pulse = 0 @ +width;
}
}
Debouncer
Debounce a noisy input signal:
mod debouncer {
in noisy: bit,
out clean: bit,
let stable_count: u8 = 0;
const THRESHOLD: u8 = 10;
on change(noisy) {
stable_count = 0;
}
loop {
if stable_count < THRESHOLD {
stable_count = (stable_count + 1) @ +1ms;
} else {
clean = noisy;
}
}
}
Advanced Examples
FIFO Queue
Simple first-in-first-out buffer:
mod fifo<DEPTH: u32 = 16> {
in clk: bit,
in write_en: bit,
in read_en: bit,
in data_in: u8,
out data_out: u8,
out empty: bit,
out full: bit,
let buffer: [u8; DEPTH];
let write_ptr: u32 = 0;
let read_ptr: u32 = 0;
let count: u32 = 0;
empty = count == 0;
full = count == DEPTH;
on rising(clk) {
if write_en && !full {
buffer[write_ptr] = data_in;
write_ptr = (write_ptr + 1) % DEPTH;
count = count + 1;
}
if read_en && !empty {
data_out = buffer[read_ptr];
read_ptr = (read_ptr + 1) % DEPTH;
count = count - 1;
}
}
}
Shift Register
Configurable shift register:
mod shift_reg<WIDTH: u32 = 8> {
in clk: bit,
in data_in: bit,
in load: bit,
in parallel_in: uint<WIDTH>,
out data_out: bit,
out parallel_out: uint<WIDTH>,
let reg: uint<WIDTH> = 0;
data_out = reg[WIDTH - 1];
parallel_out = reg;
on rising(clk) {
if load {
reg = parallel_in;
} else {
reg = {reg[WIDTH-2:0], data_in};
}
}
}
Running Examples
Check an example:
ted check examples/counter.ted
Compile the hello example (prototype backend):
ted compile examples/hello.ted --emit exe -o ./hello
./hello
TODO: The prototype codegen only supports literal
print(...)calls right now.
Simulate (when implemented):
ted sim counter.tedbc --cycles 100