CharlesCNorton commited on
Commit ·
8b9ff19
1
Parent(s): 35ce57e
4-bit saturating adder
Browse files- README.md +100 -0
- config.json +9 -0
- create_safetensors.py +105 -0
- model.safetensors +0 -0
README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
tags:
|
| 4 |
+
- pytorch
|
| 5 |
+
- safetensors
|
| 6 |
+
- threshold-logic
|
| 7 |
+
- neuromorphic
|
| 8 |
+
- arithmetic
|
| 9 |
+
- dsp
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# threshold-saturating-adder
|
| 13 |
+
|
| 14 |
+
4-bit unsigned saturating adder. Clamps to maximum value on overflow instead of wrapping around.
|
| 15 |
+
|
| 16 |
+
## Circuit
|
| 17 |
+
|
| 18 |
+
```
|
| 19 |
+
A[3:0] ──┐
|
| 20 |
+
├──► Sat Add ──┬──► S[3:0] (result)
|
| 21 |
+
B[3:0] ──┘ └──► saturated (overflow flag)
|
| 22 |
+
```
|
| 23 |
+
|
| 24 |
+
## Behavior
|
| 25 |
+
|
| 26 |
+
| A + B | Result | Saturated |
|
| 27 |
+
|-------|--------|-----------|
|
| 28 |
+
| ≤ 15 | A + B | 0 |
|
| 29 |
+
| > 15 | 15 | 1 |
|
| 30 |
+
|
| 31 |
+
## Examples
|
| 32 |
+
|
| 33 |
+
```
|
| 34 |
+
7 + 5 = 12, saturated=0
|
| 35 |
+
10 + 10 = 15, saturated=1 (true sum: 20)
|
| 36 |
+
15 + 15 = 15, saturated=1 (true sum: 30)
|
| 37 |
+
8 + 7 = 15, saturated=0
|
| 38 |
+
8 + 8 = 15, saturated=1 (true sum: 16)
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
## Wrapping vs Saturating
|
| 42 |
+
|
| 43 |
+
```
|
| 44 |
+
Wrapping: 15 + 1 = 0 (modulo 16)
|
| 45 |
+
Saturating: 15 + 1 = 15 (clamped)
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
## Architecture
|
| 49 |
+
|
| 50 |
+
| Component | Count | Neurons |
|
| 51 |
+
|-----------|-------|---------|
|
| 52 |
+
| Full Adders | 4 | 28 |
|
| 53 |
+
| Saturation MUXes | 4 | 12 |
|
| 54 |
+
|
| 55 |
+
**Total: 40 neurons, 124 parameters, 4 layers**
|
| 56 |
+
|
| 57 |
+
## How It Works
|
| 58 |
+
|
| 59 |
+
```
|
| 60 |
+
1. Compute A + B with standard 4-bit adder
|
| 61 |
+
2. If carry out = 1:
|
| 62 |
+
- Output 1111 (15)
|
| 63 |
+
- Set saturated = 1
|
| 64 |
+
3. Else:
|
| 65 |
+
- Output sum bits
|
| 66 |
+
- Set saturated = 0
|
| 67 |
+
```
|
| 68 |
+
|
| 69 |
+
## Applications
|
| 70 |
+
|
| 71 |
+
- **Audio DSP**: Prevents clipping distortion
|
| 72 |
+
- **Image processing**: Pixel value saturation
|
| 73 |
+
- **Neural networks**: Activation clamping
|
| 74 |
+
- **Control systems**: Prevents actuator overshoot
|
| 75 |
+
- **Fixed-point arithmetic**: Overflow protection
|
| 76 |
+
|
| 77 |
+
## Usage
|
| 78 |
+
|
| 79 |
+
```python
|
| 80 |
+
from safetensors.torch import load_file
|
| 81 |
+
|
| 82 |
+
w = load_file('model.safetensors')
|
| 83 |
+
|
| 84 |
+
# All 256 test cases verified
|
| 85 |
+
# Saturates 120 cases (where A+B > 15)
|
| 86 |
+
```
|
| 87 |
+
|
| 88 |
+
## Files
|
| 89 |
+
|
| 90 |
+
```
|
| 91 |
+
threshold-saturating-adder/
|
| 92 |
+
├── model.safetensors
|
| 93 |
+
├── create_safetensors.py
|
| 94 |
+
├── config.json
|
| 95 |
+
└── README.md
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
## License
|
| 99 |
+
|
| 100 |
+
MIT
|
config.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "threshold-saturating-adder",
|
| 3 |
+
"description": "4-bit unsigned saturating adder",
|
| 4 |
+
"inputs": 8,
|
| 5 |
+
"outputs": 5,
|
| 6 |
+
"neurons": 40,
|
| 7 |
+
"layers": 4,
|
| 8 |
+
"parameters": 124
|
| 9 |
+
}
|
create_safetensors.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from safetensors.torch import save_file
|
| 3 |
+
|
| 4 |
+
weights = {}
|
| 5 |
+
|
| 6 |
+
# 4-bit Unsigned Saturating Adder
|
| 7 |
+
# Inputs: a3,a2,a1,a0, b3,b2,b1,b0 (8 inputs)
|
| 8 |
+
# Outputs: s3,s2,s1,s0, saturated (5 outputs)
|
| 9 |
+
#
|
| 10 |
+
# If A + B > 15: output 15 (1111), saturated=1
|
| 11 |
+
# Else: output A + B, saturated=0
|
| 12 |
+
|
| 13 |
+
def add_xor(name):
|
| 14 |
+
weights[f'{name}.or.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 15 |
+
weights[f'{name}.or.bias'] = torch.tensor([-1.0], dtype=torch.float32)
|
| 16 |
+
weights[f'{name}.nand.weight'] = torch.tensor([[-1.0, -1.0]], dtype=torch.float32)
|
| 17 |
+
weights[f'{name}.nand.bias'] = torch.tensor([1.0], dtype=torch.float32)
|
| 18 |
+
weights[f'{name}.and.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 19 |
+
weights[f'{name}.and.bias'] = torch.tensor([-2.0], dtype=torch.float32)
|
| 20 |
+
|
| 21 |
+
def add_fa(name):
|
| 22 |
+
add_xor(f'{name}.xor1')
|
| 23 |
+
add_xor(f'{name}.sum')
|
| 24 |
+
weights[f'{name}.carry.weight'] = torch.tensor([[1.0, 1.0, 1.0]], dtype=torch.float32)
|
| 25 |
+
weights[f'{name}.carry.bias'] = torch.tensor([-2.0], dtype=torch.float32)
|
| 26 |
+
|
| 27 |
+
def add_mux(name):
|
| 28 |
+
weights[f'{name}.sel0.weight'] = torch.tensor([[1.0, -1.0]], dtype=torch.float32)
|
| 29 |
+
weights[f'{name}.sel0.bias'] = torch.tensor([-2.0], dtype=torch.float32)
|
| 30 |
+
weights[f'{name}.sel1.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 31 |
+
weights[f'{name}.sel1.bias'] = torch.tensor([-2.0], dtype=torch.float32)
|
| 32 |
+
weights[f'{name}.or.weight'] = torch.tensor([[1.0, 1.0]], dtype=torch.float32)
|
| 33 |
+
weights[f'{name}.or.bias'] = torch.tensor([-1.0], dtype=torch.float32)
|
| 34 |
+
|
| 35 |
+
# 4-bit ripple-carry adder
|
| 36 |
+
for i in range(4):
|
| 37 |
+
add_fa(f'fa{i}')
|
| 38 |
+
|
| 39 |
+
# Saturation logic: if carry out, output all 1s
|
| 40 |
+
# Each output bit: MUX(sum_bit, 1, cout)
|
| 41 |
+
for i in range(4):
|
| 42 |
+
add_mux(f'sat_mux{i}')
|
| 43 |
+
|
| 44 |
+
save_file(weights, 'model.safetensors')
|
| 45 |
+
|
| 46 |
+
def eval_xor(a, b):
|
| 47 |
+
return int((a or b) and not (a and b))
|
| 48 |
+
|
| 49 |
+
def eval_fa(a, b, cin):
|
| 50 |
+
x1 = eval_xor(a, b)
|
| 51 |
+
s = eval_xor(x1, cin)
|
| 52 |
+
c = int(a + b + cin >= 2)
|
| 53 |
+
return s, c
|
| 54 |
+
|
| 55 |
+
def saturating_add(a, b):
|
| 56 |
+
a_bits = [(a >> i) & 1 for i in range(4)]
|
| 57 |
+
b_bits = [(b >> i) & 1 for i in range(4)]
|
| 58 |
+
|
| 59 |
+
s_bits = []
|
| 60 |
+
c = 0
|
| 61 |
+
for i in range(4):
|
| 62 |
+
s, c = eval_fa(a_bits[i], b_bits[i], c)
|
| 63 |
+
s_bits.append(s)
|
| 64 |
+
|
| 65 |
+
cout = c
|
| 66 |
+
saturated = cout
|
| 67 |
+
|
| 68 |
+
if saturated:
|
| 69 |
+
result = 15
|
| 70 |
+
else:
|
| 71 |
+
result = sum(s_bits[i] << i for i in range(4))
|
| 72 |
+
|
| 73 |
+
return result, saturated
|
| 74 |
+
|
| 75 |
+
print("Verifying 4-bit Saturating Adder...")
|
| 76 |
+
errors = 0
|
| 77 |
+
for a in range(16):
|
| 78 |
+
for b in range(16):
|
| 79 |
+
result, sat = saturating_add(a, b)
|
| 80 |
+
true_sum = a + b
|
| 81 |
+
if true_sum > 15:
|
| 82 |
+
expected = 15
|
| 83 |
+
exp_sat = 1
|
| 84 |
+
else:
|
| 85 |
+
expected = true_sum
|
| 86 |
+
exp_sat = 0
|
| 87 |
+
|
| 88 |
+
if result != expected or sat != exp_sat:
|
| 89 |
+
errors += 1
|
| 90 |
+
if errors <= 5:
|
| 91 |
+
print(f"ERROR: {a}+{b} = {result} (sat={sat}), expected {expected} (sat={exp_sat})")
|
| 92 |
+
|
| 93 |
+
if errors == 0:
|
| 94 |
+
print("All 256 test cases passed!")
|
| 95 |
+
else:
|
| 96 |
+
print(f"FAILED: {errors} errors")
|
| 97 |
+
|
| 98 |
+
print("\nExamples:")
|
| 99 |
+
print(" 7 + 5 = 12, saturated=0")
|
| 100 |
+
print(" 10 + 10 = 15, saturated=1 (would be 20)")
|
| 101 |
+
print(" 15 + 15 = 15, saturated=1 (would be 30)")
|
| 102 |
+
|
| 103 |
+
mag = sum(t.abs().sum().item() for t in weights.values())
|
| 104 |
+
print(f"\nMagnitude: {mag:.0f}")
|
| 105 |
+
print(f"Parameters: {sum(t.numel() for t in weights.values())}")
|
model.safetensors
ADDED
|
Binary file (6.43 kB). View file
|
|
|