Adds tools/neural_sim/, a NumPy-based reference implementation of the FPGA-Neural V2 numeric model (INT8 in/weight, 32-bit wraparound accumulation, ReLU+saturate out), derived directly from hardware/v2/rtl/neural_processor.v (not assumed) and reusing tools/validation/mac_oracle.py's own pre-existing, hand-verified two's-complement primitives rather than duplicating them. Provides: neuron/layer/network models, a logical memory model of the real V2 SDRAM map (weights/activations/results), deterministic test-vector generators (simple/signed/extremes/zero/random/D-Stress 256x128) with JSON golden-vector export, an FPGA-vs-Python bit-exact comparison utility, four example networks, a CLI (`python -m tools.neural_sim ...`), and a 96-test pytest suite (all passing) covering signed-arithmetic edge cases (including a direct 32-bit wraparound proof), scalar-vs-vectorized neuron cross-checks, layer/memory/vector/comparison tests. This is a golden functional reference (bit-exact numeric result), explicitly NOT a cycle-accurate FPGA simulator -- see tools/neural_sim/README.md for the full scope statement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
64 lines
1.8 KiB
Python
64 lines
1.8 KiB
Python
import pytest
|
|
|
|
from tools.neural_sim.memory import (
|
|
MemoryModel, WEIGHTS_BASE, ACTIVATIONS_BASE, RESULTS_BASE, SDRAM_SIZE,
|
|
)
|
|
|
|
|
|
def test_read_after_write_each_region():
|
|
mem = MemoryModel()
|
|
mem.write_weight(0, -5)
|
|
mem.write_activation(0, 42)
|
|
mem.write_result(0, -128)
|
|
assert mem.read_weight(0) == -5
|
|
assert mem.read_activation(0) == 42
|
|
assert mem.read_result(0) == -128
|
|
|
|
|
|
def test_regions_are_at_the_real_v2_addresses():
|
|
mem = MemoryModel()
|
|
mem.write_weight(0, 1)
|
|
mem.write_activation(0, 2)
|
|
mem.write_result(0, 3)
|
|
assert mem.read_byte(WEIGHTS_BASE) == 1
|
|
assert mem.read_byte(ACTIVATIONS_BASE) == 2
|
|
assert mem.read_byte(RESULTS_BASE) == 3
|
|
|
|
|
|
def test_adjacent_regions_do_not_corrupt_each_other():
|
|
mem = MemoryModel()
|
|
mem.write_weight(0x0FFFFF - WEIGHTS_BASE, 0x11) # last word before activations
|
|
mem.write_activation(0, 0x22) # first word of activations
|
|
assert mem.read_weight(0x0FFFFF - WEIGHTS_BASE) == 0x11
|
|
assert mem.read_activation(0) == 0x22
|
|
|
|
|
|
def test_out_of_range_byte_raises():
|
|
mem = MemoryModel()
|
|
with pytest.raises(IndexError):
|
|
mem.read_byte(-1)
|
|
with pytest.raises(IndexError):
|
|
mem.read_byte(SDRAM_SIZE)
|
|
|
|
|
|
def test_value_out_of_int8_range_raises():
|
|
mem = MemoryModel()
|
|
with pytest.raises(ValueError):
|
|
mem.write_byte(0, 128)
|
|
with pytest.raises(ValueError):
|
|
mem.write_byte(0, -129)
|
|
|
|
|
|
def test_region_bounds_checking_rejects_spillover():
|
|
mem = MemoryModel()
|
|
weights_size = ACTIVATIONS_BASE - WEIGHTS_BASE
|
|
with pytest.raises(IndexError):
|
|
mem.write_weight(weights_size, 0) # one byte past the weights region
|
|
|
|
|
|
def test_bulk_helpers_round_trip():
|
|
mem = MemoryModel()
|
|
values = list(range(-10, 10))
|
|
mem.write_weights(0, values)
|
|
assert mem.read_weights(0, len(values)) == values
|