HDL Generation
Verilog RTL generation from Python SNN descriptions.
generate_verilog() — Convert SC layer/neuron descriptions to synthesizable Verilog modules. Supports Q8.8 fixed-point, LFSR encoders, popcount trees, and event-driven AER.
- IR compiler pipeline: Python → intermediate representation → SystemVerilog / MLIR (CIRCT backend)
19 hand-written Verilog modules + equation-to-Verilog compiler for arbitrary ODEs.
from sc_neurocore.hdl_gen import generate_verilog
sc_neurocore.hdl_gen
sc_neurocore.hdl_gen -- Tier: research (experimental / research).
VerilogGenerator
Generates Top-Level Verilog for a defined SC Network.
Source code in src/sc_neurocore/hdl_gen/verilog_generator.py
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
84
85 | class VerilogGenerator:
"""
Generates Top-Level Verilog for a defined SC Network.
"""
def __init__(self, module_name="sc_network_top") -> None:
self.module_name = module_name
self.layers = []
self.wires = []
self.instances = []
def add_layer(self, layer_type: str, name: str, params: Dict[str, Any]) -> None:
self.layers.append({"type": layer_type, "name": name, "params": params})
def generate(self) -> str:
"""
Emits Verilog code.
"""
code = f"module {self.module_name} (\n"
code += " input wire clk,\n"
code += " input wire rst_n,\n"
# Determine I/O from first/last layer logic (simplified)
code += " input wire [7:0] input_bus,\n"
code += " output wire [7:0] output_bus\n"
code += ");\n\n"
code += " // Internal Signals\n"
# Generate wires for connections
for i in range(len(self.layers) - 1):
code += f" wire [7:0] layer_{i}_to_{i + 1};\n"
code += "\n"
# Instantiate Layers
for i, layer in enumerate(self.layers):
l_type = layer["type"]
l_name = layer["name"]
# Simple Dense Layer instantiation logic
if l_type == "Dense":
code += f" // Layer {i}: {l_name}\n"
code += " sc_dense_layer_core #(\n"
code += f" .NUM_NEURONS({layer['params'].get('n_neurons', 10)})\n"
code += f" ) {l_name}_inst (\n"
code += " .clk(clk),\n"
code += " .rst_n(rst_n),\n"
# Connect Input
if i == 0:
code += " .input_bus(input_bus),\n"
else:
code += f" .input_bus(layer_{i - 1}_to_{i}),\n"
# Connect Output
if i == len(self.layers) - 1:
code += " .output_bus(output_bus)\n"
else:
code += f" .output_bus(layer_{i}_to_{i + 1})\n"
code += " );\n\n"
code += "endmodule\n"
return code
def save_to_file(self, path: str) -> None:
try:
with open(path, "w") as f:
f.write(self.generate())
except OSError as exc:
logger.error("Failed to write Verilog to %s: %s", path, exc)
raise
|
generate()
Emits Verilog code.
Source code in src/sc_neurocore/hdl_gen/verilog_generator.py
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 | def generate(self) -> str:
"""
Emits Verilog code.
"""
code = f"module {self.module_name} (\n"
code += " input wire clk,\n"
code += " input wire rst_n,\n"
# Determine I/O from first/last layer logic (simplified)
code += " input wire [7:0] input_bus,\n"
code += " output wire [7:0] output_bus\n"
code += ");\n\n"
code += " // Internal Signals\n"
# Generate wires for connections
for i in range(len(self.layers) - 1):
code += f" wire [7:0] layer_{i}_to_{i + 1};\n"
code += "\n"
# Instantiate Layers
for i, layer in enumerate(self.layers):
l_type = layer["type"]
l_name = layer["name"]
# Simple Dense Layer instantiation logic
if l_type == "Dense":
code += f" // Layer {i}: {l_name}\n"
code += " sc_dense_layer_core #(\n"
code += f" .NUM_NEURONS({layer['params'].get('n_neurons', 10)})\n"
code += f" ) {l_name}_inst (\n"
code += " .clk(clk),\n"
code += " .rst_n(rst_n),\n"
# Connect Input
if i == 0:
code += " .input_bus(input_bus),\n"
else:
code += f" .input_bus(layer_{i - 1}_to_{i}),\n"
# Connect Output
if i == len(self.layers) - 1:
code += " .output_bus(output_bus)\n"
else:
code += f" .output_bus(layer_{i}_to_{i + 1})\n"
code += " );\n\n"
code += "endmodule\n"
return code
|
SpiceGenerator
Generates SPICE netlists for Memristive Crossbars.
Source code in src/sc_neurocore/hdl_gen/spice_generator.py
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 | class SpiceGenerator:
"""
Generates SPICE netlists for Memristive Crossbars.
"""
@staticmethod
def generate_crossbar(weights: np.ndarray[Any, Any], filename: str) -> None:
"""
weights: (Rows, Cols) - Conductance values [0, 1] mapped to [G_off, G_on].
"""
rows, cols = weights.shape
g_on = 100e-6 # 100 uS (10 kOhm)
g_off = 1e-6 # 1 uS (1 MOhm)
netlist = f"* Memristor Crossbar {rows}x{cols}\n"
netlist += ".PARAM VDD=1.0\n\n"
# Inputs
for r in range(rows):
netlist += f"Vin_{r} in_{r} 0 DC 0.0\n"
# Memristors
for r in range(rows):
for c in range(cols):
w = weights[r, c]
g = g_off + w * (g_on - g_off)
r_val = 1.0 / g
netlist += f"R_{r}_{c} in_{r} out_{c} {r_val:.2f}\n"
# Outputs (current sensing ideally, here just nodes)
# Add load resistors
for c in range(cols):
netlist += f"Rload_{c} out_{c} 0 1k\n"
netlist += "\n.END\n"
with open(filename, "w") as f:
f.write(netlist)
logger.info("SPICE Netlist saved to %s", filename)
|
generate_crossbar(weights, filename)
staticmethod
weights: (Rows, Cols) - Conductance values [0, 1] mapped to [G_off, G_on].
Source code in src/sc_neurocore/hdl_gen/spice_generator.py
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 | @staticmethod
def generate_crossbar(weights: np.ndarray[Any, Any], filename: str) -> None:
"""
weights: (Rows, Cols) - Conductance values [0, 1] mapped to [G_off, G_on].
"""
rows, cols = weights.shape
g_on = 100e-6 # 100 uS (10 kOhm)
g_off = 1e-6 # 1 uS (1 MOhm)
netlist = f"* Memristor Crossbar {rows}x{cols}\n"
netlist += ".PARAM VDD=1.0\n\n"
# Inputs
for r in range(rows):
netlist += f"Vin_{r} in_{r} 0 DC 0.0\n"
# Memristors
for r in range(rows):
for c in range(cols):
w = weights[r, c]
g = g_off + w * (g_on - g_off)
r_val = 1.0 / g
netlist += f"R_{r}_{c} in_{r} out_{c} {r_val:.2f}\n"
# Outputs (current sensing ideally, here just nodes)
# Add load resistors
for c in range(cols):
netlist += f"Rload_{c} out_{c} 0 1k\n"
netlist += "\n.END\n"
with open(filename, "w") as f:
f.write(netlist)
logger.info("SPICE Netlist saved to %s", filename)
|