Signal Module #
This module defines the stream-based signal semantics for Sparkle HDL.
Overview #
Signals represent time-varying hardware values using infinite streams.
A Signal d α is essentially a function Nat → α where Nat represents
discrete time steps (clock cycles).
Key Concepts #
- Stream: An infinite sequence
Nat → αrepresenting values over time - Signal: A stream tagged with a clock domain for type safety
- Domain: Type-level clock domain tracking prevents mixing signals from different clocks
Core Primitives #
Registers #
Use Signal.register init input to create state elements (delays by 1 cycle):
-- Simple register chain (feed-forward)
def registerChain (input : Signal Domain (BitVec 8)) : Signal Domain (BitVec 8) :=
let d1 := Signal.register 0#8 input
let d2 := Signal.register 0#8 d1
d2
-- Counter with feedback (requires let rec)
def counter {dom : DomainConfig} : Signal dom (BitVec 8) :=
let rec count := Signal.register 0#8 (count.map (· + 1))
count
Multiplexers #
Use Signal.mux for conditional logic (NOT if-then-else):
def conditionalInc (enable : Signal Domain Bool) (input : Signal Domain (BitVec 8))
: Signal Domain (BitVec 8) :=
let next := input.map (· + 1)
Signal.mux enable next input -- Select between increment or hold
Simulation #
Signals can be simulated directly to verify behavior before synthesis:
#eval Signal.simulate myCircuit inputs |>.take 10
See also: Sparkle.Core.Domain for clock domain configuration.
Stream is an infinite sequence of values indexed by natural numbers. Time 0 is the initial state, time 1 is after first clock cycle, etc.
Equations
- Sparkle.Core.Signal.Stream α = (Nat → α)
Instances For
Signal represents a time-varying value in a specific clock domain. It wraps a Stream and carries domain information at the type level.
The domain parameter ensures signals from different clock domains cannot be accidentally mixed.
- val : Stream α
Instances For
Access the value of a signal at a specific time
Instances For
Create a constant signal (same value at all times)
Equations
- Sparkle.Core.Signal.Signal.pure x = { val := fun (x_1 : Nat) => x }
Instances For
Map a function over a signal (combinational logic)
Instances For
Register (D Flip-Flop) primitive.
At time 0: outputs the initial value At time t > 0: outputs the input value from time (t-1)
This implements a single-cycle delay, the fundamental building block of sequential logic.
Equations
Instances For
Register with enable signal.
When enable is true: register updates normally When enable is false: register holds its current value
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
Instances For
Helper to create a signal from a stream
Equations
- Sparkle.Core.Signal.Signal.fromStream s = { val := s }
Instances For
Helper to extract stream from signal
Instances For
Sample a signal for the first n cycles
Equations
- s.sample n = List.map s.val (List.range n)
Instances For
Equations
- Sparkle.Core.Signal.instFunctorSignal = { map := fun {α β : Type ?u.14} => Sparkle.Core.Signal.Signal.map }
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Sparkle.Core.Signal.instHAddSignalBitVec = { hAdd := fun (a b : Sparkle.Core.Signal.Signal dom (BitVec n)) => (fun (x1 x2 : BitVec n) => x1 + x2) <$> a <*> b }
Equations
- Sparkle.Core.Signal.instHSubSignalBitVec = { hSub := fun (a b : Sparkle.Core.Signal.Signal dom (BitVec n)) => (fun (x1 x2 : BitVec n) => x1 - x2) <$> a <*> b }
Equations
- Sparkle.Core.Signal.instHMulSignalBitVec = { hMul := fun (a b : Sparkle.Core.Signal.Signal dom (BitVec n)) => (fun (x1 x2 : BitVec n) => x1 * x2) <$> a <*> b }
Equations
- Sparkle.Core.Signal.instHAndSignalBitVec = { hAnd := fun (a b : Sparkle.Core.Signal.Signal dom (BitVec n)) => (fun (x1 x2 : BitVec n) => x1 &&& x2) <$> a <*> b }
Equations
- Sparkle.Core.Signal.instHOrSignalBitVec = { hOr := fun (a b : Sparkle.Core.Signal.Signal dom (BitVec n)) => (fun (x1 x2 : BitVec n) => x1 ||| x2) <$> a <*> b }
Equations
- Sparkle.Core.Signal.instHXorSignalBitVec = { hXor := fun (a b : Sparkle.Core.Signal.Signal dom (BitVec n)) => (fun (x1 x2 : BitVec n) => x1 ^^^ x2) <$> a <*> b }
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- One or more equations did not get rendered due to their size.
Equations
- Sparkle.Core.Signal.instHAndSignalBool = { hAnd := fun (a b : Sparkle.Core.Signal.Signal dom Bool) => (fun (x1 x2 : Bool) => x1 && x2) <$> a <*> b }
Equations
- Sparkle.Core.Signal.instHOrSignalBool = { hOr := fun (a b : Sparkle.Core.Signal.Signal dom Bool) => (fun (x1 x2 : Bool) => x1 || x2) <$> a <*> b }
Equations
- Sparkle.Core.Signal.instHXorSignalBool = { hXor := fun (a b : Sparkle.Core.Signal.Signal dom Bool) => (fun (x1 x2 : Bool) => x1 ^^ x2) <$> a <*> b }
Equations
- Sparkle.Core.Signal.instComplementSignalBool = { complement := fun (a : Sparkle.Core.Signal.Signal dom Bool) => (fun (x : Bool) => !x) <$> a }
Equations
- Sparkle.Core.Signal.instComplementSignalBitVec = { complement := fun (a : Sparkle.Core.Signal.Signal dom (BitVec n)) => (fun (x : BitVec n) => ~~~x) <$> a }
Equations
- Sparkle.Core.Signal.instHShiftLeftSignalBitVec_1 = { hShiftLeft := fun (a b : Sparkle.Core.Signal.Signal dom (BitVec n)) => (fun (x1 x2 : BitVec n) => x1 <<< x2) <$> a <*> b }
Equations
- Sparkle.Core.Signal.instHShiftRightSignalBitVec_1 = { hShiftRight := fun (a b : Sparkle.Core.Signal.Signal dom (BitVec n)) => (fun (x1 x2 : BitVec n) => x1 >>> x2) <$> a <*> b }
Unsigned less-than: a <ₛ b on signals.
Instances For
Unsigned less-or-equal: a ≤ₛ b on signals.
Instances For
Arithmetic shift right on BitVec signals.
Instances For
Instances For
Equations
- Sparkle.Core.Signal.instNegSignalBitVec = { neg := fun (a : Sparkle.Core.Signal.Signal dom (BitVec n)) => (fun (x : BitVec n) => -x) <$> a }
Equations
- One or more equations did not get rendered due to their size.
Instances For
Constant signal with explicit domain binding.
Use instead of Signal.pure when the domain can't be inferred:
let rnd := Signal.lit dom 32#16 instead of let rnd := Signal.pure 32#16
Equations
Instances For
Equations
- Sparkle.Core.Signal.instCoeBitVecSignal = { coe := fun (x : BitVec n) => Sparkle.Core.Signal.Signal.pure x }
Instances For
Equations
- Sparkle.Core.Signal.instCoeBoolSignal = { coe := fun (x : Bool) => Sparkle.Core.Signal.Signal.pure x }
Instances For
Lift a binary operation to signals (combinational logic)
Equations
- Sparkle.Core.Signal.Signal.lift2 f sa sb = f <$> sa <*> sb
Instances For
Delay a signal by n cycles, filling with initial value
Equations
Instances For
Create a signal that counts up from 0
Instances For
Mux (multiplexer): select between two signals based on condition
Equations
Instances For
The pure fixpoint value of a feedback loop, computed by strong recursion on the time index.
To compute the value at time t, we apply the loop body f to a
signal that recursively supplies the already-computed values for
every earlier cycle i < t and default at t and beyond. For a
strictly causal f (every feedback path runs through a
Signal.register, so the output at t reads only inputs strictly
before t), the default placeholder at ≥ t is never observed,
so this is exactly the loop's fixpoint — see loop_unfold in
Sparkle/Verification/LoopProps.lean, now a theorem rather than
an axiom.
The if i < t guard makes the recursion well-founded (t
strictly decreases), so this is a total, axiom-free definition.
Runtime note: this naive form recomputes the < t prefix on every
cycle (O(t) work per cycle → O(n²) for an n-cycle sim). The
@[implemented_by loopImpl] on loop below swaps in the
memoizing loopImpl for execution, keeping O(n); the pure
loopGo/loop definitions are what the kernel and the proofs
see.
Equations
Instances For
Fixed-point combinator for feedback loops.
Logically this is the pure loopGo fixpoint (no axiom, no
opaque, no unsafe). For execution it is compiled to the
memoizing loopImpl via @[implemented_by], which keeps
cycle-by-cycle simulation at O(n) instead of the O(n²) the naive
loopGo would cost.
Equations
Instances For
Chained conditional mux: priority-encoded multiplexer.
Signal.cond [(c1, v1), (c2, v2), ...] default selects the first
matching condition's value, falling back to default.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Hardware switch: replaces deeply nested Signal.mux chains.
Default value comes first, then condition/value pairs (first match wins):
hw_cond fsmReg
| startAndIdle => (1#4 : Signal dom _)
| stemDone => (2#4 : Signal dom _)
expands to Signal.mux startAndIdle (1#4) (Signal.mux stemDone (2#4) fsmReg)
Equations
- One or more equations did not get rendered due to their size.
Instances For
Arithmetic shift right with BitVec shift amount.
Wraps BitVec.sshiftRight (which takes Nat) so it can be used
in the applicative Signal DSL pattern: (ashr · ·) <$> a <*> b
Equations
- Sparkle.Core.Signal.ashr a b = a.sshiftRight b.toNat
Instances For
Instances For
Equations
Instances For
Unbundle a signal of pairs.
⚠️ Returns a Lean-level tuple. Pattern-matching on the result
(let (a, b) := unbundle2 signal) silently breaks in synthesis because the
tuple is destructured at elaboration time. Use Signal.fst / Signal.snd
directly instead. This binding is kept only so legacy test files still
compile.
Equations
Instances For
Equations
Instances For
Equations
Instances For
Unbundle a 3-tuple signal.
⚠️ Same caveat as unbundle2: the returned Lean tuple cannot be pattern-matched
in synthesis code. Use Signal.proj3_1 / proj3_2 / proj3_3 instead.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Project first element from a 3-tuple signal
Instances For
Project second element from a 3-tuple signal
Instances For
Project third element from a 3-tuple signal
Instances For
Unbundle a 4-tuple signal.
⚠️ Same caveat as unbundle2. Use Signal.proj4_1..proj4_4 instead.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Unbundle a 5-tuple signal
Equations
- One or more equations did not get rendered due to their size.
Instances For
Unbundle a 6-tuple signal
Equations
- One or more equations did not get rendered due to their size.
Instances For
Unbundle a 7-tuple signal
Equations
- One or more equations did not get rendered due to their size.
Instances For
Project the i-th element (0-indexed) from a right-nested pair signal.
projN! state n i extracts element i from n-element nested pair.
Example (4-element tuple (A × (B × (C × D)))):
projN! s 4 0 → Signal.fst s -- A
projN! s 4 1 → Signal.fst (Signal.snd s) -- B
projN! s 4 2 → Signal.fst (Signal.snd (Signal.snd s)) -- C
projN! s 4 3 → Signal.snd (Signal.snd (Signal.snd s)) -- D (last uses snd)
Equations
- One or more equations did not get rendered due to their size.
Instances For
Destructure a 2-element right-nested pair signal into named bindings.
hw_let (a, b) := sig; body expands to:
let a := Signal.fst sig; let b := Signal.snd sig; body
Equations
- One or more equations did not get rendered due to their size.
Instances For
Destructure a 3-element right-nested pair signal (A × (B × C)).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Destructure a 4-element right-nested pair signal (A × (B × (C × D))).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Verilog-style slice: bitsHL inst hi lo ≡ inst[hi,lo].
Equations
- Sparkle.Core.Signal.bitsHL x hi lo = BitVec.extractLsb' lo (hi - lo + 1) x
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- Sparkle.Core.Signal.instHasBitSliceBitVec = { slice := fun (x : BitVec w) (hi lo : Nat) => BitVec.extractLsb' lo (hi - lo + 1) x }
Equations
- One or more equations did not get rendered due to their size.