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