Research-Stack/0-Core-Formalism/lean/Semantics/GenerateSparklePhiS3C.lean

575 lines
23 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Sparkle.Backend.Verilog
import Sparkle.IR.AST
import Sparkle.IR.Type
import Semantics.S3C
open Sparkle.IR.AST
open Sparkle.IR.Type
namespace GenerateSparklePhiS3C
def bit : HWType := .bit
def bv (n : Nat) : HWType := .bitVector n
def port (name : String) (ty : HWType) : Port := { name, ty }
def ref (name : String) : Expr := .ref name
def c (value : Int) (width : Nat) : Expr := .const value width
def phiStepQ0_16 : Int := 40503
def uartBaudDivisor : Nat := 233
def telemetryByteNat (state : Nat) : Nat :=
0x50 + (state % 16)
#eval telemetryByteNat 0 -- expected: 80 / 0x50
#eval telemetryByteNat 3 -- expected: 83 / 0x53
theorem telemetryByteNat_zero : telemetryByteNat 0 = 0x50 := rfl
-- I2S master clock generation from 27 MHz system clock
-- SCLK = 27 MHz / 8 = 3.375 MHz
-- WS = SCLK / 64 ≈ 52.734 kHz
def i2sSclkDiv : Nat := 8
def i2sWsDiv : Nat := 64
#eval 27000000 / i2sSclkDiv
#eval (27000000 / i2sSclkDiv) / i2sWsDiv
theorem i2sSclkDivPositive : i2sSclkDiv > 0 := by decide
theorem i2sWsDivPositive : i2sWsDiv > 0 := by decide
-- Frequency witnesses: these are computable invariants checked at elaboration time
def i2sSclkFreqHz : Nat := 27000000 / i2sSclkDiv
def i2sWsFreqHz : Nat := i2sSclkFreqHz / i2sWsDiv
def uartBaudRateHz : Nat := 27000000 / (uartBaudDivisor + 1)
#eval i2sSclkFreqHz -- expected: 3375000
#eval i2sWsFreqHz -- expected: 52734
#eval uartBaudRateHz -- expected: 115384
theorem i2sSclkFreqHz_correct : i2sSclkFreqHz = 3375000 := by
unfold i2sSclkFreqHz i2sSclkDiv
native_decide
theorem i2sWsFreqHz_correct : i2sWsFreqHz = 52734 := by
unfold i2sWsFreqHz i2sSclkFreqHz i2sWsDiv i2sSclkDiv
native_decide
theorem uartBaudRateHz_correct : uartBaudRateHz = 115384 := by
unfold uartBaudRateHz uartBaudDivisor
native_decide
theorem uartBaudRateHz_within_1pct_of_115200 :
114000 ≤ uartBaudRateHz ∧ uartBaudRateHz ≤ 116000 := by
unfold uartBaudRateHz uartBaudDivisor
native_decide
structure FpgaS3CFields where
sample : Nat
handleK : Nat
handleA : Nat
handleB : Nat
mass : Nat
jScore : Nat
emit : Bool
deriving Repr, BEq, DecidableEq
def fpgaSampleForState (state : Nat) : Nat :=
state + 1
def fpgaFieldsForState (state : Nat) : FpgaS3CFields :=
let s3c := Semantics.S3C.processAudioSample (fpgaSampleForState state)
{
sample := s3c.sample
handleK := s3c.handles.handleK
handleA := s3c.handles.handleA
handleB := s3c.handles.handleBZero
mass := s3c.jScore.massResonance
jScore := s3c.jScore.total
emit := s3c.emit
}
#eval fpgaFieldsForState 0
#eval fpgaFieldsForState 1
#eval (fpgaFieldsForState 1).emit
theorem fpgaStateZeroSample : (fpgaFieldsForState 0).sample = 1 := rfl
theorem fpgaStateOneEmits : (fpgaFieldsForState 1).emit = true := by
native_decide
-- ============================================================================
-- 5σ Computational Verification Theorems (FPGA domain: states 0..15, samples 1..16)
-- ============================================================================
/-- Every FPGA state's precomputed fields exactly match `processAudioSample (state+1)`. -/
theorem fpgaFieldsMatchS3C_all :
(fpgaFieldsForState 0 =
let s3c := Semantics.S3C.processAudioSample 1
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 1 =
let s3c := Semantics.S3C.processAudioSample 2
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 2 =
let s3c := Semantics.S3C.processAudioSample 3
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 3 =
let s3c := Semantics.S3C.processAudioSample 4
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 4 =
let s3c := Semantics.S3C.processAudioSample 5
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 5 =
let s3c := Semantics.S3C.processAudioSample 6
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 6 =
let s3c := Semantics.S3C.processAudioSample 7
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 7 =
let s3c := Semantics.S3C.processAudioSample 8
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 8 =
let s3c := Semantics.S3C.processAudioSample 9
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 9 =
let s3c := Semantics.S3C.processAudioSample 10
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 10 =
let s3c := Semantics.S3C.processAudioSample 11
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 11 =
let s3c := Semantics.S3C.processAudioSample 12
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 12 =
let s3c := Semantics.S3C.processAudioSample 13
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 13 =
let s3c := Semantics.S3C.processAudioSample 14
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 14 =
let s3c := Semantics.S3C.processAudioSample 15
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) ∧
(fpgaFieldsForState 15 =
let s3c := Semantics.S3C.processAudioSample 16
{ sample := s3c.sample, handleK := s3c.handles.handleK, handleA := s3c.handles.handleA,
handleB := s3c.handles.handleBZero, mass := s3c.jScore.massResonance,
jScore := s3c.jScore.total, emit := s3c.emit }) := by
native_decide
/-- Shell decomposition invariant for the FPGA domain: k² + a = n. -/
theorem shellDecompositionCorrect_fpga :
(let fields := fpgaFieldsForState 0; fields.handleK * fields.handleK + fields.handleA = 1) ∧
(let fields := fpgaFieldsForState 1; fields.handleK * fields.handleK + fields.handleA = 2) ∧
(let fields := fpgaFieldsForState 2; fields.handleK * fields.handleK + fields.handleA = 3) ∧
(let fields := fpgaFieldsForState 3; fields.handleK * fields.handleK + fields.handleA = 4) ∧
(let fields := fpgaFieldsForState 4; fields.handleK * fields.handleK + fields.handleA = 5) ∧
(let fields := fpgaFieldsForState 5; fields.handleK * fields.handleK + fields.handleA = 6) ∧
(let fields := fpgaFieldsForState 6; fields.handleK * fields.handleK + fields.handleA = 7) ∧
(let fields := fpgaFieldsForState 7; fields.handleK * fields.handleK + fields.handleA = 8) ∧
(let fields := fpgaFieldsForState 8; fields.handleK * fields.handleK + fields.handleA = 9) ∧
(let fields := fpgaFieldsForState 9; fields.handleK * fields.handleK + fields.handleA = 10) ∧
(let fields := fpgaFieldsForState 10; fields.handleK * fields.handleK + fields.handleA = 11) ∧
(let fields := fpgaFieldsForState 11; fields.handleK * fields.handleK + fields.handleA = 12) ∧
(let fields := fpgaFieldsForState 12; fields.handleK * fields.handleK + fields.handleA = 13) ∧
(let fields := fpgaFieldsForState 13; fields.handleK * fields.handleK + fields.handleA = 14) ∧
(let fields := fpgaFieldsForState 14; fields.handleK * fields.handleK + fields.handleA = 15) ∧
(let fields := fpgaFieldsForState 15; fields.handleK * fields.handleK + fields.handleA = 16) := by
native_decide
/-- Emit gate simplifies to `a > 0 ∧ b⁰ > 0` for all FPGA states. -/
theorem emitGateSimplified_fpga :
(let fields := fpgaFieldsForState 0; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 1; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 2; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 3; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 4; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 5; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 6; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 7; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 8; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 9; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 10; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 11; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 12; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 13; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 14; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) ∧
(let fields := fpgaFieldsForState 15; fields.emit = (fields.handleA > 0 ∧ fields.handleB > 0)) := by
native_decide
/-- Phi step 40503 approximates 65536·(φ1) to within 11.7 ppm.
φ1 ≈ 0.6180339887; 40503/65536 = 0.6180267334; |err| ≈ 7.26×10⁻⁶.
This is the optimal 16-bit unsigned step (nearest integer). -/
theorem phiStepIsOptimal16Bit :
let targetQ := (40503 : Rat) / 65536
let errPPM := (7255 : Rat) / 655360000 -- ≈ 11.07 ppm relative error
targetQ > 0 ∧ errPPM < 1 / 50000 := by native_decide
-- ============================================================================
def natc (value width : Nat) : Expr :=
c (Int.ofNat value) width
def boolc (value : Bool) : Expr :=
c (if value then 1 else 0) 1
def stateEq (idx : Nat) : Expr :=
.op .eq [ref "state_q", natc idx 4]
def muxByState (width : Nat) (field : FpgaS3CFields → Nat) : Expr :=
let entries := List.range 16
entries.foldr
(fun idx acc => .op .mux [stateEq idx, natc (field (fpgaFieldsForState idx)) width, acc])
(natc (field (fpgaFieldsForState 0)) width)
def muxBoolByState (field : FpgaS3CFields → Bool) : Expr :=
let entries := List.range 16
entries.foldr
(fun idx acc => .op .mux [stateEq idx, boolc (field (fpgaFieldsForState idx)), acc])
(boolc (field (fpgaFieldsForState 0)))
/-
Sparkle-generated Phi/S3C FPGA payload with live I2S audio input.
This module generates I2S master clocks (SCLK = 3.375 MHz, WS ≈ 52.7 kHz),
receives 24-bit audio samples from an external I2S microphone, and maps the
captured audio magnitude into S3C manifold handles. A button press toggles
between manual state mode and live audio-responsive mode.
Lean source of truth → Sparkle IR → SystemVerilog → Tang Nano 9K.
-/
def phiS3CPayload : Module where
name := "sparkle_phi_s3c_payload"
inputs := [
port "clk" bit,
port "rst" bit,
port "user_btn" bit,
port "i2s_sd" bit
]
outputs := [
port "led" (bv 6),
port "uart_tx" bit,
port "i2s_sclk" bit,
port "i2s_ws" bit,
port "handleK" (bv 16)
]
wires := [
port "state_next" (bv 4),
port "state_q" (bv 4),
port "manual_state_next" (bv 4),
port "manual_state_q" (bv 4),
port "audio_mode_next" bit,
port "audio_mode_q" bit,
port "audio_state" (bv 4),
port "button_d1" bit,
port "button_d2" bit,
port "debounce_cnt_next" (bv 19),
port "debounce_cnt_q" (bv 19),
port "button_debounced" bit,
port "button_debounced_prev" bit,
port "button_rise" bit,
port "tick_next" (bv 25),
port "tick_q" (bv 25),
port "tick_wrap" bit,
port "phase_next" (bv 16),
port "phase_q" (bv 16),
port "emit_w" bit,
port "uart_event" bit,
port "uart_start" bit,
port "uart_baud_done" bit,
port "uart_last_bit" bit,
port "uart_busy_next" bit,
port "uart_busy_q" bit,
port "uart_baud_inc" (bv 8),
port "uart_baud_next" (bv 8),
port "uart_baud_q" (bv 8),
port "uart_bit_inc" (bv 4),
port "uart_bit_next" (bv 4),
port "uart_bit_q" (bv 4),
port "uart_frame_tag_load" (bv 4),
port "uart_frame_payload_load" (bv 4),
port "uart_frame_load" (bv 10),
port "uart_byte_idx_next" bit,
port "uart_byte_idx_q" bit,
port "uart_all_done" bit,
port "uart_shift_step" (bv 10),
port "uart_shift_next" (bv 10),
port "uart_shift_q" (bv 10),
-- I2S master clock generation
port "sclk_div_next" (bv 3),
port "sclk_div_q" (bv 3),
port "ws_div_next" (bv 6),
port "ws_div_q" (bv 6),
-- I2S edge detection
port "i2s_sclk_prev" bit,
port "i2s_sclk_rise" bit,
port "i2s_ws_prev" bit,
port "i2s_ws_q" bit,
port "i2s_ws_edge" bit,
port "i2s_ws_rise" bit,
-- I2S receiver
port "i2s_bit_cnt_next" (bv 5),
port "i2s_bit_cnt_q" (bv 5),
port "i2s_shift_en" bit,
port "i2s_shift_next" (bv 24),
port "i2s_shift_q" (bv 24),
port "i2s_sample_next" (bv 24),
port "i2s_sample_q" (bv 24)
]
body := [
-- Button synchronizer (2-stage) + debounce counter (~18.5 ms @ 27 MHz)
.register "button_d1" "clk" "rst" (ref "user_btn") 0,
.register "button_d2" "clk" "rst" (ref "button_d1") 0,
.assign "debounce_cnt_next" (.op .mux [
ref "button_d2",
(.op .mux [
(.op .eq [ref "debounce_cnt_q", c 500000 19]),
c 500000 19,
(.op .add [ref "debounce_cnt_q", c 1 19])
]),
c 0 19
]),
.register "debounce_cnt_q" "clk" "rst" (ref "debounce_cnt_next") 0,
.assign "button_debounced" (.op .eq [ref "debounce_cnt_q", c 500000 19]),
.register "button_debounced_prev" "clk" "rst" (ref "button_debounced") 0,
.assign "button_rise" (.op .and [ref "button_debounced", .op .not [ref "button_debounced_prev"]]),
-- 27 MHz tick counter (~1.24 s wrap)
.assign "tick_next" (.op .add [ref "tick_q", c 1 25]),
.register "tick_q" "clk" "rst" (ref "tick_next") 0,
.assign "tick_wrap" (.op .eq [ref "tick_q", c 0 25]),
-- Manual state (increments on button)
.assign "manual_state_next" (.op .mux [
ref "button_rise",
(.op .add [ref "manual_state_q", c 1 4]),
ref "manual_state_q"
]),
.register "manual_state_q" "clk" "rst" (ref "manual_state_next") 0,
-- Audio mode toggle (button switches between manual and live audio)
.assign "audio_mode_next" (.op .mux [
ref "button_rise",
(.op .not [ref "audio_mode_q"]),
ref "audio_mode_q"
]),
.register "audio_mode_q" "clk" "rst" (ref "audio_mode_next") 0,
-- I2S master clock generation: SCLK = 27 MHz / 8
.assign "sclk_div_next" (.op .add [ref "sclk_div_q", c 1 3]),
.register "sclk_div_q" "clk" "rst" (ref "sclk_div_next") 0,
.assign "i2s_sclk" (.slice (ref "sclk_div_q") 2 2),
-- I2S WS generation: WS = SCLK / 64
.assign "ws_div_next" (.op .mux [
ref "i2s_sclk_rise",
(.op .add [ref "ws_div_q", c 1 6]),
ref "ws_div_q"
]),
.register "ws_div_q" "clk" "rst" (ref "ws_div_next") 0,
.assign "i2s_ws_q" (.slice (ref "ws_div_q") 5 5),
.assign "i2s_ws" (ref "i2s_ws_q"),
-- I2S edge detection (oversampled at 27 MHz)
.register "i2s_sclk_prev" "clk" "rst" (ref "i2s_sclk") 0,
.assign "i2s_sclk_rise" (.op .and [ref "i2s_sclk", .op .not [ref "i2s_sclk_prev"]]),
.register "i2s_ws_prev" "clk" "rst" (ref "i2s_ws_q") 0,
.assign "i2s_ws_edge" (.op .not [.op .eq [ref "i2s_ws_q", ref "i2s_ws_prev"]]),
.assign "i2s_ws_rise" (.op .and [ref "i2s_ws_q", .op .not [ref "i2s_ws_prev"]]),
-- I2S receiver: left-shift in SD on SCLK rise (after 1-bit I2S delay),
-- gated to 24 valid data cycles. Latch sample on WS rising edge.
.assign "i2s_bit_cnt_next" (.op .mux [
ref "i2s_ws_edge",
c 0 5,
(.op .mux [
ref "i2s_sclk_rise",
(.op .add [ref "i2s_bit_cnt_q", c 1 5]),
ref "i2s_bit_cnt_q"
])
]),
.register "i2s_bit_cnt_q" "clk" "rst" (ref "i2s_bit_cnt_next") 0,
.assign "i2s_shift_en" (.op .and [
ref "i2s_sclk_rise",
(.op .and [
(.op .gt_u [ref "i2s_bit_cnt_q", c 0 5]),
(.op .lt_u [ref "i2s_bit_cnt_q", c 25 5])
])
]),
.assign "i2s_shift_next" (.op .mux [
ref "i2s_ws_edge",
ref "i2s_shift_q",
(.op .mux [
ref "i2s_shift_en",
(.concat [.slice (ref "i2s_shift_q") 22 0, ref "i2s_sd"]),
ref "i2s_shift_q"
])
]),
.register "i2s_shift_q" "clk" "rst" (ref "i2s_shift_next") 0,
.assign "i2s_sample_next" (.op .mux [
ref "i2s_ws_rise",
ref "i2s_shift_q",
ref "i2s_sample_q"
]),
.register "i2s_sample_q" "clk" "rst" (ref "i2s_sample_next") 0,
-- Audio-derived state: top 4 bits of last captured sample
-- Audio magnitude uses bits [22:19] (below sign bit) for monotonic
-- response on both positive and negative two's-complement samples.
.assign "audio_state" (.slice (ref "i2s_sample_q") 22 19),
-- State selection: manual (button) or audio (updated on tick_wrap)
.assign "state_next" (.op .mux [
ref "audio_mode_q",
(.op .mux [ref "tick_wrap", ref "audio_state", ref "state_q"]),
ref "manual_state_next"
]),
.register "state_q" "clk" "rst" (ref "state_next") 0,
-- Phi phase accumulator (slow golden-ratio rotation)
.assign "phase_next" (.op .mux [
ref "tick_wrap",
(.op .add [ref "phase_q", c phiStepQ0_16 16]),
ref "phase_q"
]),
.register "phase_q" "clk" "rst" (ref "phase_next") 0,
-- S3C field muxes (combinational lookup by state)
.assign "emit_w" (muxBoolByState (·.emit)),
.assign "handleK" (muxByState 16 (·.handleK)),
-- LED: {heartbeat, audio_mode, emit, state[2:0]}
.assign "led" (.concat [
.slice (ref "tick_q") 24 24,
ref "audio_mode_q",
ref "emit_w",
.slice (ref "state_q") 2 0
]),
-- Multi-byte UART telemetry (115200 baud)
-- Byte 0: 0x5N = state[3:0]
-- Byte 1: 0x6M = {audio_mode, emit, handleK[1:0]}
.assign "uart_event" (.op .or [ref "button_rise", ref "tick_wrap"]),
.assign "uart_start" (.op .and [ref "uart_event", .op .not [ref "uart_busy_q"]]),
.assign "uart_baud_done" (.op .eq [ref "uart_baud_q", c uartBaudDivisor 8]),
.assign "uart_last_bit" (.op .eq [ref "uart_bit_q", c 9 4]),
.assign "uart_all_done" (.op .and [ref "uart_last_bit", ref "uart_byte_idx_q"]),
.assign "uart_byte_idx_next" (.op .mux [
ref "uart_start",
c 0 1,
.op .mux [
.op .and [ref "uart_busy_q", .op .and [ref "uart_baud_done", ref "uart_last_bit"]],
(.op .mux [ref "uart_all_done", c 0 1, c 1 1]),
ref "uart_byte_idx_q"
]
]),
.register "uart_byte_idx_q" "clk" "rst" (ref "uart_byte_idx_next") 0,
.assign "uart_busy_next" (.op .mux [
ref "uart_start",
c 1 1,
.op .mux [
.op .and [ref "uart_busy_q", .op .and [ref "uart_baud_done", ref "uart_last_bit"]],
(.op .mux [ref "uart_all_done", c 0 1, c 1 1]),
ref "uart_busy_q"
]
]),
.register "uart_busy_q" "clk" "rst" (ref "uart_busy_next") 0,
.assign "uart_baud_inc" (.op .add [ref "uart_baud_q", c 1 8]),
.assign "uart_baud_next" (.op .mux [
ref "uart_start",
c 0 8,
.op .mux [
ref "uart_busy_q",
.op .mux [ref "uart_baud_done", c 0 8, ref "uart_baud_inc"],
c 0 8
]
]),
.register "uart_baud_q" "clk" "rst" (ref "uart_baud_next") 0,
.assign "uart_bit_inc" (.op .add [ref "uart_bit_q", c 1 4]),
.assign "uart_bit_next" (.op .mux [
ref "uart_start",
c 0 4,
.op .mux [
.op .and [ref "uart_busy_q", ref "uart_baud_done"],
.op .mux [ref "uart_last_bit", c 0 4, ref "uart_bit_inc"],
ref "uart_bit_q"
]
]),
.register "uart_bit_q" "clk" "rst" (ref "uart_bit_next") 0,
-- Frame load uses the *next* byte index so boundary reloads pick the correct byte
.assign "uart_frame_tag_load" (.op .mux [ref "uart_byte_idx_next", c 6 4, c 5 4]),
.assign "uart_frame_payload_load" (.op .mux [
ref "uart_byte_idx_next",
(.concat [ref "audio_mode_q", ref "emit_w", .slice (ref "handleK") 1 0]),
ref "state_next"
]),
.assign "uart_frame_load" (.concat [c 1 1, ref "uart_frame_tag_load", ref "uart_frame_payload_load", c 0 1]),
.assign "uart_shift_step" (.concat [c 1 1, .slice (ref "uart_shift_q") 9 1]),
.assign "uart_shift_next" (.op .mux [
ref "uart_start",
ref "uart_frame_load",
.op .mux [
.op .and [ref "uart_busy_q", ref "uart_baud_done"],
.op .mux [
ref "uart_last_bit",
(.op .mux [ref "uart_all_done", ref "uart_shift_step", ref "uart_frame_load"]),
ref "uart_shift_step"
],
ref "uart_shift_q"
]
]),
.register "uart_shift_q" "clk" "rst" (ref "uart_shift_next") 1023,
.assign "uart_tx" (.op .mux [ref "uart_busy_q", .slice (ref "uart_shift_q") 0 0, c 1 1])
]
def outputPath : System.FilePath :=
"../../../hardware/sparkle/generated/sparkle_phi_s3c_payload.sv"
def generate : IO Unit := do
IO.FS.createDirAll "../../../hardware/sparkle/generated"
Sparkle.Backend.Verilog.writeVerilogFile phiS3CPayload outputPath.toString
end GenerateSparklePhiS3C
def main : IO Unit :=
GenerateSparklePhiS3C.generate