Skip to content

Commit cf600c0

Browse files
CiM Updates, add CiM tutorial arch, add Albireo (optical/photonic computing) arch
1 parent c8cf7a6 commit cf600c0

18 files changed

Lines changed: 21583 additions & 324 deletions

examples/arches/compute_in_memory/_include.yaml

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,39 @@
1+
input_sliced: &input_sliced
2+
bits_per_value: {input: average_input_bits_per_slice}
3+
actions_scale: n_input_slices
4+
5+
weight_sliced: &weight_sliced
6+
# Weights now operating sliced; each slice is a subset of the bits of the full weight.
7+
bits_per_value: {weight: average_weight_bits_per_slice}
8+
# Each weight slice is a different action
9+
actions_scale: n_weight_slices
10+
11+
both_sliced: &both_sliced
12+
# Weights and inputs are both sliced; each slice is a subset of the bits of the full
13+
# value.
14+
bits_per_value: {input: average_input_bits_per_slice, weight: average_weight_bits_per_slice}
15+
# Each weight & input slice combo is a different action.
16+
actions_scale: n_input_slices * n_weight_slices
17+
18+
both_sliced_weight_slices_parallelized: &both_sliced_weight_slices_parallelized
19+
# Weights and inputs are both sliced; each slice is a subset of the bits of the full
20+
# value.
21+
bits_per_value: {input: average_input_bits_per_slice, weight: average_weight_bits_per_slice}
22+
23+
# Each weight & input slice combo is a different action.
24+
actions_scale: n_input_slices * n_weight_slices
25+
26+
# Hopefully the architecture already has all of its hardware instances defined and
27+
# already uses usage_scale to account for weight slices consuming more array
28+
# utilization. So they should process the weight slices in parallel, scaling the
29+
# latency by 1 / n_weight_slices.
30+
latency_scale: 1 / n_weight_slices
31+
32+
one_action_per_value: &one_action_per_value
33+
values_per_action: {All: 1}
34+
135
cim_component_attributes: &cim_component_attributes
2-
# These are for NeuroSim
36+
# These are for NeuroSim.
337
rows: array_wordlines
438
cols: array_bitlines
539
cols_active_at_once: array_parallel_outputs
@@ -17,7 +51,7 @@ cim_component_attributes: &cim_component_attributes
1751
n_adcs: n_adc_per_bank
1852
width: encoded_output_bits
1953

20-
# These will be applied to the weight drivers
54+
# These will be applied to the weight drivers.
2155
weight_drivers_attributes: &weight_drivers_attributes
2256
<<: *cim_component_attributes
2357
rows: array_wordlines
@@ -33,19 +67,19 @@ weight_drivers_attributes: &weight_drivers_attributes
3367
read_pulse_width: 0 # Irrelevant for weight programming
3468

3569
variables_global: &variables_global
36-
weight_bits: weight.bits_per_value
37-
input_bits: input.bits_per_value
38-
output_bits: output.bits_per_value
70+
weight_bits: round(weight.bits_per_value)
71+
input_bits: round(input.bits_per_value)
72+
output_bits: round(output.bits_per_value)
3973
array_parallel_inputs: get_array_fanout_reuse_output(spec)
4074
array_parallel_outputs: get_array_fanout_reuse_input(spec)
4175
array_parallel_weights: get_array_fanout_total(spec)
42-
array_wordlines: array_parallel_inputs * cim_unit_width_cells
43-
array_bitlines: array_parallel_outputs * cim_unit_depth_cells
76+
array_wordlines: array_parallel_inputs * cim_unit_depth_cells
77+
array_bitlines: array_parallel_outputs * cim_unit_width_cells
4478
dac_resolution: max(voltage_dac_resolution, temporal_dac_resolution)
4579
cols_active_at_once: array_parallel_outputs
4680

4781
# Calculate the number of slices needed to store the input and weight bits and
48-
# the number of bits in each slice
82+
# the number of bits in each slice.
4983
in_b: encoded_input_bits # Shorthands so the following lines aren't super long
5084
w_b: encoded_weight_bits
5185
max_input_bits_per_slice: min(dac_resolution, in_b)
@@ -57,10 +91,10 @@ variables_global: &variables_global
5791
average_weight_bits_per_sliced_psum: encoded_weight_bits / n_sliced_psums
5892
average_output_bits_per_sliced_psum: encoded_output_bits / n_sliced_psums
5993

60-
# This is for the bitwise-multiplication of the input and weight slices
94+
# This is for the bitwise-multiplication of the input and weight slices.
6195
n_virtual_macs: max_input_bits_per_slice * max_weight_bits_per_slice * encoded_output_bits
6296

63-
# Calculate statistics for input and weight values and bits after encoding
97+
# Calculate statistics for input and weight values and bits after encoding.
6498
ehtas: encoded_hist_to_avg_slice # Shorthands so the following lines aren't super long
6599
in_enc_fn: input_encoding_func
66100
w_enc_fn: weight_encoding_func

examples/arches/compute_in_memory/_include_functions.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,37 @@
11
import accelforge as af
22

33

4+
def _eval_fanout(fanout, spec: af.Spec) -> int:
5+
if isinstance(fanout, (int, float)):
6+
return int(fanout)
7+
if isinstance(fanout, str):
8+
symbol_table = {}
9+
try:
10+
symbol_table.update(dict(spec.variables))
11+
except Exception:
12+
pass
13+
try:
14+
symbol_table.update(dict(spec.arch.variables))
15+
except Exception:
16+
pass
17+
try:
18+
return int(eval(fanout, {}, symbol_table))
19+
except Exception:
20+
raise ValueError(
21+
f"Could not evaluate fanout expression '{fanout}' "
22+
f"with symbol table {symbol_table}."
23+
)
24+
return int(fanout)
25+
26+
427
def get_array_fanout_reuse_input(spec: af.Spec) -> int:
528
"""Get total fanout of array spatial dims that reuse input (= columns)."""
629
n = 1
730
for leaf in spec.arch.get_nodes_of_type(af.arch.Leaf):
831
for sp in leaf.spatial:
932
if sp.name.endswith("ARRAY_COLUMNS") or sp.name.endswith("ARRAY_ROWS"):
1033
if str(sp.may_reuse) == "input" or str(sp.reuse) == "input":
11-
n *= sp.fanout
34+
n *= _eval_fanout(sp.fanout, spec)
1235
return n
1336

1437

@@ -19,7 +42,7 @@ def get_array_fanout_reuse_output(spec: af.Spec) -> int:
1942
for sp in leaf.spatial:
2043
if sp.name.endswith("ARRAY_COLUMNS") or sp.name.endswith("ARRAY_ROWS"):
2144
if str(sp.may_reuse) == "output" or str(sp.reuse) == "output":
22-
n *= sp.fanout
45+
n *= _eval_fanout(sp.fanout, spec)
2346
return n
2447

2548

@@ -200,7 +223,7 @@ def encoded_hist_to_avg_slice(
200223
return_per_slice: bool = False,
201224
):
202225
if isinstance(bits_per_slice, int):
203-
bits_per_slice = [bits_per_slice] * (total_bits // bits_per_slice)
226+
bits_per_slice = [bits_per_slice] * round(total_bits // bits_per_slice)
204227
if sum(bits_per_slice) != total_bits:
205228
bits_per_slice.append(total_bits - sum(bits_per_slice))
206229

examples/arches/compute_in_memory/_load_spec.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ def get_spec(
4343
workload = af.Workload.from_yaml(arch_name, top_key="workload")
4444
spec = af.Spec(arch=arch, variables=variables, workload=workload)
4545

46+
# Helpful to increase spatial utilization. Especially helpful for CiM accelerators,
47+
# which are often running conv workloads & have weird hardware and/or workload
48+
# shapes that are difficult to perfectly tile.
49+
spec.mapper.explore_imperfect_spatial_loops = True
50+
51+
# It's OK to explore loop orders, but will make the search take longer, and is not
52+
# very helpful for CiM accelerators. It's not very helpful because loop orders help
53+
# us capture sliding window reuse, and the CiM macros consume very big tiles at
54+
# once, giving limited benefit from the small overlap between each tile.
55+
spec.mapper.explore_loop_orders = False
56+
57+
spec.mapper.metrics = af.Metrics.ENERGY_DELAY_PRODUCT
58+
4659
spec.config.expression_custom_functions.append(
4760
os.path.join(THIS_SCRIPT_DIR, "_include_functions.py")
4861
)

0 commit comments

Comments
 (0)