Skip to main content

sc_neurocore_engine/
encoder.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Commercial license available
3// © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
4// © Code 2020–2026 Miroslav Šotek. All rights reserved.
5// ORCID: 0009-0009-3560-0851
6// Contact: www.anulum.li | protoscience@anulum.li
7// SC-NeuroCore — Stochastic Encoders
8
9//! # Stochastic Encoders
10//!
11//! LFSR-driven Bernoulli encoding utilities used by SC-NeuroCore kernels.
12
13/// 16-bit linear-feedback shift register used as pseudo-random source.
14#[derive(Clone, Debug)]
15pub struct Lfsr16 {
16    /// Current register value.
17    pub reg: u16,
18    /// Register width in bits.
19    pub width: u32,
20}
21
22impl Lfsr16 {
23    /// Create an LFSR with a non-zero seed.
24    pub fn new(seed: u16) -> Self {
25        assert_ne!(seed, 0, "LFSR seed must be non-zero.");
26        Self {
27            reg: seed,
28            width: 16,
29        }
30    }
31
32    /// Advance one LFSR step and return the new register value.
33    pub fn step(&mut self) -> u16 {
34        let feedback =
35            ((self.reg >> 15) ^ (self.reg >> 13) ^ (self.reg >> 12) ^ (self.reg >> 10)) & 1;
36        self.reg = (self.reg << 1) | feedback;
37        self.reg
38    }
39
40    /// Reset register state. If `seed` is `None`, keeps current seed value.
41    pub fn reset(&mut self, seed: Option<u16>) {
42        let next = seed.unwrap_or(self.reg);
43        assert_ne!(next, 0, "LFSR seed must be non-zero.");
44        self.reg = next;
45    }
46}
47
48/// Comparator-based stochastic bitstream encoder.
49#[derive(Clone, Debug)]
50pub struct BitstreamEncoder {
51    /// Underlying LFSR source.
52    pub lfsr: Lfsr16,
53    /// Data path width for compatibility with fixed-point callers.
54    pub data_width: u32,
55    seed_init: u16,
56}
57
58impl BitstreamEncoder {
59    /// Create a new encoder with deterministic seed.
60    pub fn new(data_width: u32, seed: u16) -> Self {
61        Self {
62            lfsr: Lfsr16::new(seed),
63            data_width,
64            seed_init: seed,
65        }
66    }
67
68    /// Emit one stochastic bit by comparing RNG value against `x_value`.
69    ///
70    /// Matches Verilog RTL semantics: compare current register, then advance.
71    /// (Non-blocking assignment in `sc_bitstream_encoder.v` reads pre-advance state.)
72    pub fn step(&mut self, x_value: u16) -> u8 {
73        let bit = if self.lfsr.reg < x_value { 1 } else { 0 };
74        self.lfsr.step();
75        bit
76    }
77
78    /// Reset the encoder LFSR state.
79    pub fn reset(&mut self, seed: Option<u16>) {
80        let next = seed.unwrap_or(self.seed_init);
81        self.lfsr = Lfsr16::new(next);
82    }
83}