catalyst-n1 / visualize.py
mrwabbit's picture
Initial upload: Catalyst N1 open source neuromorphic processor RTL
e4cdd5f verified
"""
Neuromorphic Chip - Spike Visualizer
Parses the VCD waveform file and generates visual plots of neuron activity.
"""
import re
import os
def parse_vcd_spikes(vcd_path):
"""Parse VCD file to extract spike timing for each neuron."""
spikes = {0: [], 1: [], 2: [], 3: []}
membrane = {0: [], 1: [], 2: [], 3: []}
current_time = 0
id_map = {}
with open(vcd_path, 'r') as f:
in_header = True
for line in f:
line = line.strip()
# Parse variable declarations
if line.startswith('$var'):
parts = line.split()
if len(parts) >= 5:
var_id = parts[3]
var_name = parts[4]
# Map IDs to signal names
id_map[var_id] = var_name
if line == '$enddefinitions $end':
in_header = False
continue
if in_header:
continue
# Parse time changes
if line.startswith('#'):
current_time = int(line[1:])
continue
# Parse value changes for spike signals
# Single bit values: 0X or 1X where X is the identifier
if len(line) >= 2 and line[0] in ('0', '1'):
val = int(line[0])
var_id = line[1:]
if var_id in id_map:
name = id_map[var_id]
for i in range(4):
if name == f'spikes[{i}]' or (name == 'spikes' and var_id.endswith(f'[{i}]')):
if val == 1:
spikes[i].append(current_time)
return spikes, current_time
def parse_simulation_output(sim_output=None):
"""Parse spike times from simulation console output."""
spikes = {0: [], 1: [], 2: [], 3: []}
# Known spike data from our simulation
raw = """[185000] SPIKE! Neuron 0
[335000] SPIKE! Neuron 0
[485000] SPIKE! Neuron 0
[505000] SPIKE! Neuron 1
[635000] SPIKE! Neuron 0
[655000] SPIKE! Neuron 2
[785000] SPIKE! Neuron 0
[935000] SPIKE! Neuron 0
[955000] SPIKE! Neuron 1
[1085000] SPIKE! Neuron 0
[1235000] SPIKE! Neuron 0
[1255000] SPIKE! Neuron 2
[1385000] SPIKE! Neuron 0
[1405000] SPIKE! Neuron 1
[1535000] SPIKE! Neuron 0
[1685000] SPIKE! Neuron 0
[1835000] SPIKE! Neuron 0
[1855000] SPIKE! Neuron 1
[1855000] SPIKE! Neuron 2
[1875000] SPIKE! Neuron 3
[1895000] SPIKE! Neuron 0
[2045000] SPIKE! Neuron 0
[2145000] SPIKE! Neuron 0
[2165000] SPIKE! Neuron 1
[2245000] SPIKE! Neuron 0
[2265000] SPIKE! Neuron 2
[2345000] SPIKE! Neuron 0
[2445000] SPIKE! Neuron 0
[2465000] SPIKE! Neuron 1
[2545000] SPIKE! Neuron 0
[2645000] SPIKE! Neuron 0
[2665000] SPIKE! Neuron 2
[2745000] SPIKE! Neuron 0
[2765000] SPIKE! Neuron 1
[2845000] SPIKE! Neuron 0
[2945000] SPIKE! Neuron 0
[3045000] SPIKE! Neuron 0
[3065000] SPIKE! Neuron 1
[3065000] SPIKE! Neuron 2
[3085000] SPIKE! Neuron 3
[3105000] SPIKE! Neuron 0
[3205000] SPIKE! Neuron 0
[3305000] SPIKE! Neuron 0
[3325000] SPIKE! Neuron 1
[3405000] SPIKE! Neuron 0
[3425000] SPIKE! Neuron 2
[3505000] SPIKE! Neuron 0
[3605000] SPIKE! Neuron 0
[3625000] SPIKE! Neuron 1
[3705000] SPIKE! Neuron 0
[3805000] SPIKE! Neuron 0
[3825000] SPIKE! Neuron 2
[3905000] SPIKE! Neuron 0
[3925000] SPIKE! Neuron 1
[4005000] SPIKE! Neuron 0
[4105000] SPIKE! Neuron 0
[4105000] SPIKE! Neuron 2
[4125000] SPIKE! Neuron 3
[4205000] SPIKE! Neuron 0
[4215000] SPIKE! Neuron 2
[4225000] SPIKE! Neuron 1
[4305000] SPIKE! Neuron 0
[4325000] SPIKE! Neuron 2
[4405000] SPIKE! Neuron 0
[4425000] SPIKE! Neuron 2
[4445000] SPIKE! Neuron 3
[4465000] SPIKE! Neuron 0
[4485000] SPIKE! Neuron 1
[4515000] SPIKE! Neuron 2
[4565000] SPIKE! Neuron 0
[4605000] SPIKE! Neuron 2
[4665000] SPIKE! Neuron 0
[4695000] SPIKE! Neuron 2
[4715000] SPIKE! Neuron 3
[4785000] SPIKE! Neuron 0
[4805000] SPIKE! Neuron 1
[4805000] SPIKE! Neuron 2
[4885000] SPIKE! Neuron 0
[4905000] SPIKE! Neuron 2
[4985000] SPIKE! Neuron 0
[5005000] SPIKE! Neuron 2
[5025000] SPIKE! Neuron 3
[5045000] SPIKE! Neuron 0
[5065000] SPIKE! Neuron 1
[5095000] SPIKE! Neuron 2
[5145000] SPIKE! Neuron 0
[5185000] SPIKE! Neuron 2
[5245000] SPIKE! Neuron 0
[5275000] SPIKE! Neuron 2
[5295000] SPIKE! Neuron 3
[5365000] SPIKE! Neuron 0
[5385000] SPIKE! Neuron 1
[5385000] SPIKE! Neuron 2
[5465000] SPIKE! Neuron 0
[5485000] SPIKE! Neuron 2
[5565000] SPIKE! Neuron 0
[5585000] SPIKE! Neuron 2
[5605000] SPIKE! Neuron 3
[5625000] SPIKE! Neuron 0
[5645000] SPIKE! Neuron 1
[5675000] SPIKE! Neuron 2
[5725000] SPIKE! Neuron 0
[5765000] SPIKE! Neuron 2
[5825000] SPIKE! Neuron 0
[5855000] SPIKE! Neuron 2
[5875000] SPIKE! Neuron 3
[5945000] SPIKE! Neuron 0
[5965000] SPIKE! Neuron 1
[5965000] SPIKE! Neuron 2
[6045000] SPIKE! Neuron 0
[6065000] SPIKE! Neuron 2"""
for line in raw.strip().split('\n'):
m = re.match(r'\[(\d+)\] SPIKE! Neuron (\d)', line)
if m:
time_ps = int(m.group(1))
neuron = int(m.group(2))
spikes[neuron].append(time_ps)
return spikes
def draw_raster_plot(spikes, total_time=7070000):
"""Draw a text-based spike raster plot."""
width = 100 # characters wide
neuron_names = ['Neuron 0 (Input) ', 'Neuron 1 (Excit) ', 'Neuron 2 (Chain) ', 'Neuron 3 (Inhibit) ']
neuron_chars = ['#', '+', '*', 'o']
# Phase markers
phases = [
(70000, 'Phase 1: Low stimulus'),
(2070000, 'Phase 2: High stimulus'),
(4070000, 'Phase 3: Dual stimulus'),
(6070000, 'Phase 4: No stimulus'),
]
print()
print('=' * (width + 25))
print(' NEUROMORPHIC CHIP - SPIKE RASTER PLOT')
print(' Each mark = one spike from that neuron')
print('=' * (width + 25))
print()
# Time axis header
header = ' '
for i in range(0, width + 1, 20):
time_us = (i / width) * (total_time / 1000)
header += f'{time_us:>6.0f}us' + ' ' * 12
print(header)
print(' ' + '-' * width)
# Draw phase markers
phase_line = ' '
for t, name in phases:
pos = int((t / total_time) * width)
phase_line = phase_line[:20+pos] + '|' + phase_line[21+pos:]
print(phase_line)
# Draw each neuron's spike train
for n in range(4):
line = neuron_names[n]
row = [' '] * width
for spike_time in spikes[n]:
pos = int((spike_time / total_time) * width)
if 0 <= pos < width:
row[pos] = neuron_chars[n]
line += ''.join(row) + f' ({len(spikes[n])} spikes)'
print(line)
print(' ' + '-' * width)
# Phase labels
print()
print(' Phases:')
for t, name in phases:
print(f' | {name} (t={t/1000:.0f}us)')
print()
print(' Circuit:')
print(' External Input --> [N0] --excit--> [N1]')
print(' |')
print(' +---excit--> [N2] --excit--> [N3]')
print(' | |')
print(' +<--------inhibit--------------+')
print()
# Firing rate analysis
print(' Firing Rate Analysis:')
for phase_idx in range(len(phases)):
t_start = phases[phase_idx][0]
t_end = phases[phase_idx + 1][0] if phase_idx + 1 < len(phases) else total_time
duration_us = (t_end - t_start) / 1000
print(f' {phases[phase_idx][1]}:')
for n in range(4):
count = sum(1 for s in spikes[n] if t_start <= s < t_end)
rate = (count / duration_us) * 1000 if duration_us > 0 else 0
bar = '#' * int(rate * 2)
print(f' N{n}: {count:>3} spikes ({rate:>5.1f} spikes/ms) {bar}')
print()
def draw_membrane_ascii(spikes, total_time=7070000):
"""Draw a simplified membrane potential visualization."""
width = 100
height = 10
print('=' * (width + 25))
print(' MEMBRANE POTENTIAL APPROXIMATION (Neuron 0)')
print(' Threshold = 1000 (top line)')
print('=' * (width + 25))
print()
# Simulate membrane potential for neuron 0
threshold = 1000
leak = 2
input_current = 0
potential = 0
potentials = []
for t in range(0, total_time, total_time // width):
# Determine current phase input
if t < 70000:
input_current = 0
elif t < 2070000:
input_current = 100
elif t < 4070000:
input_current = 200
elif t < 6070000:
input_current = 200
else:
input_current = 0
# Check if there's a spike near this time
spiked = any(abs(s - t) < (total_time // width) for s in spikes[0])
if spiked:
potentials.append(threshold)
potential = 0
else:
potential = min(potential + input_current - leak, threshold)
potential = max(potential, 0)
potentials.append(potential)
# Draw
for row in range(height, -1, -1):
level = (row / height) * threshold
line = f' {level:>6.0f} |'
for col in range(min(width, len(potentials))):
if potentials[col] >= level and (row == 0 or potentials[col] < ((row + 1) / height) * threshold):
line += '#'
elif potentials[col] >= level:
line += '|'
elif row == height and potentials[col] >= threshold * 0.95:
line += '^' # spike marker
else:
line += ' '
print(line)
print(f' +' + '-' * width)
print(f' 0us' + ' ' * (width - 20) + f'{total_time/1000:.0f}us')
print()
if __name__ == '__main__':
print('\n' * 2)
# Parse spikes from simulation output
spikes = parse_simulation_output()
# Draw visualizations
draw_raster_plot(spikes)
draw_membrane_ascii(spikes)
print('To view full waveforms with GTKWave:')
print(' wsl gtkwave /mnt/c/Users/mrwab/neuromorphic-chip/sim/neuron_core.vcd')
print()