accelforge 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. accelforge/__init__.py +21 -0
  2. accelforge/_accelerated_imports.py +16 -0
  3. accelforge/_deprecate/_simanneal/evalmapping.py +271 -0
  4. accelforge/_deprecate/_simanneal/mapspaceglobals.py +298 -0
  5. accelforge/_deprecate/_simanneal/simanneal.py +666 -0
  6. accelforge/_deprecate/_simanneal/tracking.py +105 -0
  7. accelforge/_deprecate/_simanneal/wrappers.py +218 -0
  8. accelforge/_deprecate/_simanneal2/__init__.py +7 -0
  9. accelforge/_deprecate/_simanneal2/simanneal.py +493 -0
  10. accelforge/_deprecate/_simanneal2/tracking.py +116 -0
  11. accelforge/_deprecate/compatibility_util.py +181 -0
  12. accelforge/_deprecate/layerdeduplication/__init__.py +2 -0
  13. accelforge/_deprecate/layerdeduplication/group_similar_einsums.py +160 -0
  14. accelforge/_deprecate/layerdeduplication/grouped_einsums.py +84 -0
  15. accelforge/_deprecate/mapping_filter_tags/__init__.py +2 -0
  16. accelforge/_deprecate/mapping_filter_tags/ffmt.py +212 -0
  17. accelforge/_deprecate/mapping_filter_tags/onesplit.py +24 -0
  18. accelforge/_deprecate/mapping_filter_tags/util.py +24 -0
  19. accelforge/_deprecate/tags.py +69 -0
  20. accelforge/_deprecate/viz/__init__.py +0 -0
  21. accelforge/_deprecate/viz/interactive.py +159 -0
  22. accelforge/_deprecate/viz/reservationtree.py +307 -0
  23. accelforge/_deprecate/viz/ski_slope.py +88 -0
  24. accelforge/_version.py +15 -0
  25. accelforge/examples.py +39 -0
  26. accelforge/frontend/__init__.py +10 -0
  27. accelforge/frontend/_binding.py +129 -0
  28. accelforge/frontend/_workload_isl/__init__.py +2 -0
  29. accelforge/frontend/_workload_isl/_isl.py +149 -0
  30. accelforge/frontend/_workload_isl/_symbolic.py +141 -0
  31. accelforge/frontend/arch copy.py +1544 -0
  32. accelforge/frontend/arch.py +1642 -0
  33. accelforge/frontend/config.py +63 -0
  34. accelforge/frontend/mapper/__init__.py +5 -0
  35. accelforge/frontend/mapper/ffm.py +126 -0
  36. accelforge/frontend/mapper/mapper.py +7 -0
  37. accelforge/frontend/mapper/metrics.py +30 -0
  38. accelforge/frontend/mapping/__init__.py +1 -0
  39. accelforge/frontend/mapping/mapping.py +1736 -0
  40. accelforge/frontend/model.py +14 -0
  41. accelforge/frontend/renames.py +150 -0
  42. accelforge/frontend/spec copy.py +230 -0
  43. accelforge/frontend/spec.py +301 -0
  44. accelforge/frontend/variables.py +12 -0
  45. accelforge/frontend/workload.py +952 -0
  46. accelforge/mapper/FFM/__init__.py +9 -0
  47. accelforge/mapper/FFM/_join_pmappings/__init__.py +0 -0
  48. accelforge/mapper/FFM/_join_pmappings/compatibility.py +653 -0
  49. accelforge/mapper/FFM/_join_pmappings/compress_pmappings.py +140 -0
  50. accelforge/mapper/FFM/_join_pmappings/join_pmappings.py +703 -0
  51. accelforge/mapper/FFM/_join_pmappings/pmapping_dataframe.py +901 -0
  52. accelforge/mapper/FFM/_join_pmappings/pmapping_group.py +337 -0
  53. accelforge/mapper/FFM/_make_pmappings/contraints/__init__.py +0 -0
  54. accelforge/mapper/FFM/_make_pmappings/contraints/constraints.py +360 -0
  55. accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/__init__.py +1 -0
  56. accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_loops.py +373 -0
  57. accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_pmapping_templates.py +463 -0
  58. accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_reservations.py +95 -0
  59. accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_storage_order.py +382 -0
  60. accelforge/mapper/FFM/_make_pmappings/make_pmapping_templates/make_storages.py +155 -0
  61. accelforge/mapper/FFM/_make_pmappings/make_pmappings.py +411 -0
  62. accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/__init__.py +1 -0
  63. accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/make_pmappings_from_templates.py +407 -0
  64. accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/make_tile_shapes.py +1681 -0
  65. accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/run_model.py +170 -0
  66. accelforge/mapper/FFM/_make_pmappings/make_pmappings_from_templates/symbol_relations.py +174 -0
  67. accelforge/mapper/FFM/_make_pmappings/pmapper_job.py +282 -0
  68. accelforge/mapper/FFM/_pareto_df/df_convention.py +273 -0
  69. accelforge/mapper/FFM/_pareto_df/pareto copy.py +836 -0
  70. accelforge/mapper/FFM/_pareto_df/pareto.py +508 -0
  71. accelforge/mapper/FFM/data.py +61 -0
  72. accelforge/mapper/FFM/main copy.py +236 -0
  73. accelforge/mapper/FFM/main.py +208 -0
  74. accelforge/mapper/FFM/mappings.py +510 -0
  75. accelforge/mapper/FFM/pmappings.py +310 -0
  76. accelforge/mapper/__init__.py +4 -0
  77. accelforge/mapper.py +0 -0
  78. accelforge/model/__init__.py +1 -0
  79. accelforge/model/_looptree/__init__.py +0 -0
  80. accelforge/model/_looptree/accesses.py +335 -0
  81. accelforge/model/_looptree/capacity/__init__.py +1 -0
  82. accelforge/model/_looptree/capacity/aggregators.py +36 -0
  83. accelforge/model/_looptree/capacity/capacity.py +47 -0
  84. accelforge/model/_looptree/energy.py +150 -0
  85. accelforge/model/_looptree/equivalent_ranks.py +29 -0
  86. accelforge/model/_looptree/latency/__init__.py +1 -0
  87. accelforge/model/_looptree/latency/latency.py +98 -0
  88. accelforge/model/_looptree/latency/memory.py +120 -0
  89. accelforge/model/_looptree/latency/processors.py +92 -0
  90. accelforge/model/_looptree/mapping_utilities.py +71 -0
  91. accelforge/model/_looptree/reuse/__init__.py +4 -0
  92. accelforge/model/_looptree/reuse/isl/__init__.py +1 -0
  93. accelforge/model/_looptree/reuse/isl/des.py +59 -0
  94. accelforge/model/_looptree/reuse/isl/isl_functions.py +374 -0
  95. accelforge/model/_looptree/reuse/isl/mapping_to_isl/__init__.py +4 -0
  96. accelforge/model/_looptree/reuse/isl/mapping_to_isl/analyze_mapping.py +297 -0
  97. accelforge/model/_looptree/reuse/isl/mapping_to_isl/skews_from_mapping.py +236 -0
  98. accelforge/model/_looptree/reuse/isl/mapping_to_isl/tiling.py +685 -0
  99. accelforge/model/_looptree/reuse/isl/mapping_to_isl/types.py +188 -0
  100. accelforge/model/_looptree/reuse/isl/spatial.py +260 -0
  101. accelforge/model/_looptree/reuse/isl/temporal.py +182 -0
  102. accelforge/model/_looptree/reuse/symbolic/__init__.py +1 -0
  103. accelforge/model/_looptree/reuse/symbolic/symbolic copy 2.py +1346 -0
  104. accelforge/model/_looptree/reuse/symbolic/symbolic copy.py +1408 -0
  105. accelforge/model/_looptree/reuse/symbolic/symbolic.py +1396 -0
  106. accelforge/model/_looptree/run.py +122 -0
  107. accelforge/model/_looptree/types.py +26 -0
  108. accelforge/model/_looptree/visualization/__init__.py +0 -0
  109. accelforge/model/_looptree/visualization/occupancy.py +11 -0
  110. accelforge/model/main.py +222 -0
  111. accelforge/plotting/__init__.py +2 -0
  112. accelforge/plotting/mappings.py +219 -0
  113. accelforge/plotting/specs.py +57 -0
  114. accelforge/util/__init__.py +4 -0
  115. accelforge/util/_base_analysis_types.py +24 -0
  116. accelforge/util/_basetypes.py +1089 -0
  117. accelforge/util/_frozenset.py +36 -0
  118. accelforge/util/_isl.py +29 -0
  119. accelforge/util/_itertools.py +14 -0
  120. accelforge/util/_mathfuncs.py +57 -0
  121. accelforge/util/_parse_expressions.py +339 -0
  122. accelforge/util/_picklecache.py +32 -0
  123. accelforge/util/_setexpressions.py +268 -0
  124. accelforge/util/_sympy/__init__.py +0 -0
  125. accelforge/util/_sympy/broadcast_max.py +18 -0
  126. accelforge/util/_visualization.py +112 -0
  127. accelforge/util/_yaml.py +579 -0
  128. accelforge/util/parallel.py +193 -0
  129. accelforge-0.0.1.dist-info/METADATA +64 -0
  130. accelforge-0.0.1.dist-info/RECORD +258 -0
  131. accelforge-0.0.1.dist-info/WHEEL +5 -0
  132. accelforge-0.0.1.dist-info/licenses/LICENSE +19 -0
  133. accelforge-0.0.1.dist-info/top_level.txt +5 -0
  134. docs/_build/html/_sources/fastfusion.frontend.mapper.rst.txt +37 -0
  135. docs/_build/html/_sources/fastfusion.frontend.rst.txt +70 -0
  136. docs/_build/html/_sources/fastfusion.frontend.workload.rst.txt +21 -0
  137. docs/_build/html/_sources/fastfusion.mapper.FFM.rst.txt +37 -0
  138. docs/_build/html/_sources/fastfusion.mapper.rst.txt +18 -0
  139. docs/_build/html/_sources/fastfusion.rst.txt +20 -0
  140. docs/_build/html/_sources/fastfusion.util.rst.txt +21 -0
  141. docs/_build/html/_sources/index.rst.txt +87 -0
  142. docs/_build/html/_sources/modules.rst.txt +7 -0
  143. docs/_build/html/_sources/notes/citation.rst.txt +45 -0
  144. docs/_build/html/_sources/notes/definitions.rst.txt +43 -0
  145. docs/_build/html/_sources/notes/faqs.rst.txt +39 -0
  146. docs/_build/html/_sources/notes/modeling/accelerator_energy_latency.rst.txt +72 -0
  147. docs/_build/html/_sources/notes/modeling/component_energy_area.rst.txt +96 -0
  148. docs/_build/html/_sources/notes/modeling/mapping.rst.txt +100 -0
  149. docs/_build/html/_sources/notes/modeling.rst.txt +33 -0
  150. docs/_build/html/_sources/notes/parsing/arithmetic_parsing.rst.txt +136 -0
  151. docs/_build/html/_sources/notes/parsing/setexpressions.rst.txt +63 -0
  152. docs/_build/html/_sources/notes/parsing/yaml_parsing.rst.txt +176 -0
  153. docs/_build/html/_sources/notes/quickstart_and_installation.rst.txt +9 -0
  154. docs/_build/html/_sources/notes/spec/architecture.rst.txt +133 -0
  155. docs/_build/html/_sources/notes/spec/mapping.rst.txt +12 -0
  156. docs/_build/html/_sources/notes/spec/workload.rst.txt +83 -0
  157. docs/_build/html/_sources/notes/spec.rst.txt +36 -0
  158. docs/source/_ext/include_attrs.py +213 -0
  159. docs/source/_ext/include_docstring.py +364 -0
  160. docs/source/_ext/include_functions.py +154 -0
  161. docs/source/_ext/include_notebook.py +131 -0
  162. docs/source/_ext/include_yaml.py +119 -0
  163. docs/source/_ext/inherited_attributes.py +222 -0
  164. docs/source/_ext/paths.py +4 -0
  165. docs/source/conf.py +79 -0
  166. examples/arches/compute_in_memory/_include.yaml +74 -0
  167. examples/arches/compute_in_memory/_include_functions.py +229 -0
  168. examples/arches/compute_in_memory/_load_spec.py +57 -0
  169. examples/arches/compute_in_memory/components/c2c_multiplier.py +181 -0
  170. examples/arches/compute_in_memory/components/dac_c2c_r2r.py +605 -0
  171. examples/arches/compute_in_memory/components/misc.py +195 -0
  172. examples/arches/compute_in_memory/components/util/bit_functions.py +51 -0
  173. examples/arches/compute_in_memory/components/zero_comparator.py +92 -0
  174. examples/arches/compute_in_memory/isaac.yaml +233 -0
  175. examples/arches/compute_in_memory/memory_cells/ecram_demo.yaml +63 -0
  176. examples/arches/compute_in_memory/memory_cells/rram_example.yaml +63 -0
  177. examples/arches/compute_in_memory/memory_cells/rram_isaac_isca_2016.yaml +64 -0
  178. examples/arches/compute_in_memory/memory_cells/rram_neurosim_default.yaml +63 -0
  179. examples/arches/compute_in_memory/memory_cells/rram_raella_isca_2023.yaml +70 -0
  180. examples/arches/compute_in_memory/memory_cells/rram_wan_nature_2022.yaml +63 -0
  181. examples/arches/compute_in_memory/memory_cells/sram_colonnade_jssc_2021.yaml +63 -0
  182. examples/arches/compute_in_memory/memory_cells/sram_example.yaml +63 -0
  183. examples/arches/compute_in_memory/memory_cells/sram_jia_jssc_2020.yaml +63 -0
  184. examples/arches/compute_in_memory/memory_cells/sram_sinangil_jssc_2021.yaml +63 -0
  185. examples/arches/compute_in_memory/memory_cells/sram_wang_vlsi_2022.yaml +63 -0
  186. examples/arches/compute_in_memory/wang_vlsi_2022.yaml +289 -0
  187. examples/arches/eyeriss.yaml +68 -0
  188. examples/arches/fanout_variations/at_glb.yaml +31 -0
  189. examples/arches/fanout_variations/at_glb_with_fanout_node.yaml +34 -0
  190. examples/arches/fanout_variations/at_mac.yaml +31 -0
  191. examples/arches/fanout_variations/at_mac_with_constraints.yaml +38 -0
  192. examples/arches/fanout_variations/at_mac_with_fanout_node.yaml +34 -0
  193. examples/arches/nvdla.yaml +47 -0
  194. examples/arches/simple.yaml +28 -0
  195. examples/arches/tpu_v4i.yaml +67 -0
  196. examples/mappings/unfused_matmuls_to_simple.yaml +33 -0
  197. examples/misc/component_annotated.yaml +33 -0
  198. examples/workloads/gpt3_6.7B.yaml +124 -0
  199. examples/workloads/matmuls.yaml +20 -0
  200. examples/workloads/mobilenet_28.yaml +81 -0
  201. examples/workloads/mobilenet_various_separate.yaml +106 -0
  202. examples/workloads/three_matmuls_annotated.yaml +59 -0
  203. notebooks/.ipynb_checkpoints/fastfusion_arch_study_michael-checkpoint.ipynb +359 -0
  204. notebooks/compute_in_memory/_scripts.py +339 -0
  205. notebooks/compute_in_memory/isaac.guide.ipynb +270 -0
  206. notebooks/compute_in_memory/wang_vlsi_2022.ipynb +602 -0
  207. notebooks/paths.py +4 -0
  208. notebooks/tutorials/.ipynb_checkpoints/1_FFM-checkpoint.ipynb +3110 -0
  209. notebooks/tutorials/FFM.ipynb +3498 -0
  210. notebooks/tutorials/_include.py +48 -0
  211. notebooks/tutorials/component_energy_area.ipynb +363 -0
  212. tests/Q_mapping.yaml +38 -0
  213. tests/__init__.py +0 -0
  214. tests/conv.mapping.yaml +27 -0
  215. tests/conv.workload.yaml +13 -0
  216. tests/conv_sym.mapping.yaml +43 -0
  217. tests/copy.mapping.yaml +35 -0
  218. tests/copy.workload.yaml +15 -0
  219. tests/distribuffers/__init__.py +0 -0
  220. tests/distribuffers/multicast/test_cases.yaml +482 -0
  221. tests/distribuffers/spec/binding/valid_bindings.yaml +97 -0
  222. tests/distribuffers/spec/distributed.yaml +100 -0
  223. tests/distribuffers/spec/logical_arch.yaml +32 -0
  224. tests/distribuffers/spec/physical_arch.yaml +69 -0
  225. tests/distribuffers/test_binding.py +48 -0
  226. tests/frontend/__init__.py +0 -0
  227. tests/frontend/test_mapping_viz.py +52 -0
  228. tests/mapper/__init__.py +0 -0
  229. tests/mapper/configs/conv1d/conv1d.mapping.yaml +31 -0
  230. tests/mapper/configs/conv1d/conv1d.workload.yaml +11 -0
  231. tests/mapper/configs/two_conv1d/two_conv1d.expected.yaml +38 -0
  232. tests/mapper/configs/two_conv1d/two_conv1d.mapping.yaml +54 -0
  233. tests/mapper/configs/two_conv1d/two_conv1d.workload.yaml +19 -0
  234. tests/mapper/test_mapping_to_isl.py +90 -0
  235. tests/mapper/test_spatial_reuse_analysis.py +67 -0
  236. tests/mapper/test_temporal_reuse_analysis.py +56 -0
  237. tests/mapper/util.py +58 -0
  238. tests/matmul.mapping.yaml +29 -0
  239. tests/matmul.workload.yaml +12 -0
  240. tests/matmul_spatial.mapping.yaml +44 -0
  241. tests/mha.renames.yaml +65 -0
  242. tests/mha.workload.yaml +67 -0
  243. tests/mha.yaml +59 -0
  244. tests/mha_full.workload.yaml +67 -0
  245. tests/mobilenet.workload.yaml +35 -0
  246. tests/mobilenet_long.workload.yaml +64 -0
  247. tests/pmappingcache.py +24 -0
  248. tests/processing_stage.arch.yaml +40 -0
  249. tests/snowcat.arch.yaml +36 -0
  250. tests/test_ffm_join_pmappings.py +106 -0
  251. tests/test_ffm_make_pmappings.py +82 -0
  252. tests/test_ffm_make_tile_shapes.py +49 -0
  253. tests/test_mapper.py +100 -0
  254. tests/test_model.py +37 -0
  255. tests/test_plotting.py +72 -0
  256. tests/test_processing_stage.py +46 -0
  257. tests/test_symbolic_model.py +248 -0
  258. tests/test_workload.py +141 -0
@@ -0,0 +1,605 @@
1
+ import math
2
+
3
+ from hwcomponents.scaling import linear
4
+ from hwcomponents_neurosim import FlipFlop
5
+ from misc import Capacitor
6
+ from typing import List
7
+
8
+ from hwcomponents import ComponentModel, action
9
+
10
+
11
+ def value2bits(value: int, resolution: int) -> List[int]:
12
+ """Converts a value to a list of bits."""
13
+ return [int(i) for i in bin(value)[2:].zfill(resolution)]
14
+
15
+
16
+ class _X2XLadderDAC(ComponentModel):
17
+ """
18
+ Base class for X2X Ladder DACs. This class is not intended to be instantiated
19
+ directly. Use a subclass.
20
+ """
21
+
22
+ priority = 0.5
23
+
24
+ def __init__(
25
+ self,
26
+ resolution: int,
27
+ voltage: float,
28
+ unit_x: float,
29
+ tech_node: int,
30
+ area: float,
31
+ leak_power: float,
32
+ kind: str,
33
+ hist: List[float] = None,
34
+ load_resistance: float = 0,
35
+ load_capacitance: float = 0,
36
+ cycle_period: float = 1e-9,
37
+ ):
38
+ self.resolution = resolution
39
+ self.voltage = voltage
40
+ self._unit_x = unit_x
41
+ self.tech_node = tech_node
42
+ self._max_value = 2**resolution - 1
43
+ self.hist = hist
44
+ self.load_resistance = load_resistance
45
+ self.load_capacitance = load_capacitance
46
+ self._kind = kind
47
+ self.cycle_period = cycle_period
48
+ self._output_cap = Capacitor(
49
+ voltage=voltage, capacitance=load_capacitance, tech_node=tech_node
50
+ )
51
+ self._flip_flops = FlipFlop(
52
+ tech_node=tech_node, cycle_period=cycle_period, n_bits=resolution
53
+ )
54
+ super().__init__(
55
+ area=self._flip_flops.area + area,
56
+ leak_power=self._flip_flops.leak_power + leak_power,
57
+ )
58
+
59
+ def _get_latency(
60
+ self,
61
+ load_capacitance: float,
62
+ load_resistance: float,
63
+ lsbs_expected_to_change: float = None,
64
+ lsbs_allowed_incorrect: float = 0,
65
+ porp_charge_loss_to_overcome: float = 0,
66
+ ) -> float:
67
+ """
68
+ Returns the latency in seconds to convert the input value to an analog voltage
69
+ and charge a load. If the latency is greater than the cycle period, the DAC will
70
+ be run more slowly in order to meet the required hold time and settle to <=0.5
71
+ LSB error. If the latency is less than the cycle period, the DAC will hold the
72
+ value for the full cycle period, and may consume more power than necessary. Note
73
+ that hold time will be longer if load_capacitance is increased or
74
+ load_resistance is decreased.
75
+
76
+ Parameters
77
+ ----------
78
+ load_capacitance: float
79
+ Load capacitance in Farads
80
+ load_resistance: float
81
+ Load resistance in Ohms
82
+ lsbs_expected_to_change: float, optional
83
+ Expected values. Lower values mean a lower-swing output. Defaults to None.
84
+ lsbs_allowed_incorrect: float, optional
85
+ Number of LSBs allowed to be incorrect in the final converted value.
86
+ Defaults to 0.
87
+ porp_charge_loss_to_overcome: float, optional
88
+ Porportion (0 to 1) of the charge on the load capacitor that is leaked
89
+ between cycles amd must be recharged. Defaults to 0.
90
+
91
+ Returns
92
+ -------
93
+ float: Latency in seconds
94
+
95
+ """
96
+ # Instant convergence if there's no load resistance
97
+ if self.load_resistance + load_resistance == 0:
98
+ return self.cycle_period
99
+
100
+ if lsbs_expected_to_change is None:
101
+ lsbs_expected_to_change = self.resolution
102
+
103
+ lsbs_allowed_incorrect += 0.5
104
+
105
+ # Time it takes for the output to converge within lsb_error_allowed LSB
106
+ r = self.load_resistance + load_resistance
107
+ c = self.load_capacitance + load_capacitance
108
+
109
+ delta = 2**lsbs_expected_to_change / self._max_value
110
+ t0 = -math.log(delta + porp_charge_loss_to_overcome) * r * c
111
+ t1 = -math.log(2**lsbs_allowed_incorrect / self._max_value) * r * c
112
+
113
+ hold_time = t1 - t0
114
+ if hold_time > self.cycle_period:
115
+ self.logger.warning(
116
+ f"Required hold time {hold_time} is greater than the cycle period "
117
+ f"{self.cycle_period}. DAC will be run more slowly in order to meet "
118
+ f"the required hold time and settle to <=0.5 LSB error. Note that hold "
119
+ f"time will be longer if load_capacitance is increased or "
120
+ f"load_resistance is decreased."
121
+ )
122
+ if hold_time < self.cycle_period:
123
+ self.logger.warning(
124
+ f"Cycle period {self.cycle_period} is greater than the required hold "
125
+ f"time {hold_time}. DAC will hold the value for the full cycle period, "
126
+ f"and may therefore consume more power than necessary. Note that hold "
127
+ f"time will be longer if load_capacitance is increased or "
128
+ f"load_resistance is decreased."
129
+ )
130
+
131
+ return max(hold_time, self.cycle_period)
132
+
133
+ def solve_for_voltage_at_each_node(self, input_value: int) -> list:
134
+ """
135
+ Solves the matrix:
136
+ [ 4 -2 0.... ][V_0]
137
+ input_voltages = [-2 5 -2 0 ..... ][V_1]
138
+ [ 0 -2 5 -2 0 .... ][V_2]
139
+ [ 0 0 -2 5 -2 0 ....][V_3]
140
+ [ ................... ][V_4]
141
+ [ 0 0 0 0 0 -2 3][V_{n-1}]
142
+
143
+
144
+ This matrix arises in C-2C and R-2R ladders. To solve, we set up the equation
145
+ for each node: 0 = (V_{i} - V_{i-1}) + (V_{i} - V_{i+1}) + (V_{i} - Input_{i}) /
146
+ 2 The first node has an additional connector, and the last node does not
147
+
148
+ Parameters
149
+ ----------
150
+ input_value: int
151
+ Input value to be converted. Must be between 0 and 2^resolution - 1.
152
+
153
+ Returns
154
+ -------
155
+ list[float]:
156
+ The voltage at each node in the ladder. The first element is the voltage at
157
+ the voltage farthest from the output, and the last element is the voltage
158
+ immediately before the output.
159
+ """
160
+ # Reverse input_value_bits to get the MSB on the right side of the circuit,
161
+ # closest to the output
162
+ input_value_bits = value2bits(input_value, self.resolution)[::-1]
163
+
164
+ lhs = [i * self.voltage for i in input_value_bits]
165
+ matrix_values = [[0, 4, -2]]
166
+ for i in range(len(lhs) - 2):
167
+ matrix_values.append([-2, 5, -2])
168
+ matrix_values.append([-2, 3, 0])
169
+
170
+ for i in range(len(lhs) - 2, -1, -1):
171
+ mult = matrix_values[i][2] / matrix_values[i + 1][1]
172
+ matrix_values[i][1] -= matrix_values[i + 1][0] * mult
173
+ matrix_values[i][2] -= matrix_values[i + 1][1] * mult
174
+ lhs[i] -= lhs[i + 1] * mult
175
+ lhs[i] /= matrix_values[i][1]
176
+ matrix_values[i] = [j / matrix_values[i][1] for j in matrix_values[i]]
177
+
178
+ for i in range(0, len(lhs) - 1):
179
+ mult = matrix_values[i + 1][0] / matrix_values[i][1]
180
+ matrix_values[i + 1][0] -= matrix_values[i][1] * mult
181
+ matrix_values[i + 1][1] -= matrix_values[i][2] * mult
182
+ lhs[i + 1] -= lhs[i] * mult
183
+
184
+ lhs = [l / matrix_values[i][1] for i, l in enumerate(lhs)]
185
+
186
+ # Un-reverse the bits
187
+ return lhs[::-1]
188
+
189
+ def _input_value_to_analog_energy_or_power(self, input_value: int) -> float:
190
+ """
191
+ Returns the energy or power to convert the input value to an analog voltage,
192
+ depending on whether unit_x is a capacitance or a resistance.
193
+
194
+ Parameters
195
+ ----------
196
+ input_value: int
197
+ Input value to be converted. Must be between 0 and 2^resolution - 1.
198
+
199
+ Returns
200
+ -------
201
+ float: Energy in Joules
202
+ """
203
+ input_value_bits = value2bits(input_value, self.resolution)
204
+ node_voltages = self.solve_for_voltage_at_each_node(input_value)
205
+ current = 0
206
+ for i, bit in enumerate(input_value_bits):
207
+ current += (self.voltage - node_voltages[i]) * (bit != 0)
208
+ energy = current * self.voltage * self._unit_x
209
+ assert energy >= 0
210
+ return energy
211
+
212
+ def _convert_energy(
213
+ self,
214
+ latency: float | None = None,
215
+ ):
216
+ """
217
+ Returns the average energy and latency to convert the input value to an analog
218
+ voltage
219
+
220
+ Parameters
221
+ ----------
222
+ latency: float | None, optional
223
+ If this is a resistive DAC, energy scales with the latency for which the
224
+ value is held. If no latency is given, energy is returned without scaling.
225
+ """
226
+ energy = 0
227
+
228
+ # This code resizes the histogram into the full distribution of values
229
+ # that this DAC can produce. It also makes sure to map the 0
230
+ # probability exactly.
231
+ newhist = [0] * 2**self.resolution
232
+ idx0 = len(self.hist) // 2
233
+ new_idx0 = len(newhist) // 2
234
+ width_scale = len(self.hist) / len(newhist)
235
+ prunedhist = [i for i in self.hist]
236
+ for exact_maps in [(0, 0), (idx0, new_idx0)]:
237
+ newhist[exact_maps[1]] = prunedhist[exact_maps[0]] / min(width_scale, 1)
238
+ prunedhist[exact_maps[0]] = 0
239
+
240
+ for i in range(len(newhist)):
241
+ if i == new_idx0 or i == 0:
242
+ continue
243
+ loc = i / len(newhist) * (len(prunedhist) - 1)
244
+ if width_scale > 1:
245
+ start = math.floor(loc)
246
+ end = min(math.ceil(start + width_scale), len(prunedhist) - 1)
247
+ for j in range(start, end):
248
+ newhist[i] += prunedhist[j]
249
+ else:
250
+ porp = loc - math.floor(loc)
251
+ newhist[i] += prunedhist[math.floor(loc)] * (1 - porp)
252
+ newhist[i] += prunedhist[math.ceil(loc)] * porp
253
+
254
+ sum_newhist = sum(newhist)
255
+ newhist = [n / sum_newhist for n in newhist]
256
+
257
+ # Calculate the energy
258
+ for i, p in enumerate(newhist):
259
+ energy += self._input_value_to_analog_energy_or_power(i) * p
260
+ energy /= sum(newhist)
261
+ assert energy >= 0
262
+
263
+ # Latency not none ->
264
+ if self._kind == "R2R":
265
+ assert latency is not None
266
+ energy *= latency
267
+
268
+ # Output cap energy
269
+ cap_e, _ = self._output_cap.switch(newhist, zero_between_values=False)
270
+ self.logger.info(f"Output capacitance consumed {cap_e}J")
271
+ assert cap_e >= 0
272
+
273
+ # Flip flop energy
274
+ flip_flops_e, _ = self._flip_flops.read()
275
+ probabilities = [0] * self.resolution
276
+ for value, probability in enumerate(newhist):
277
+ bits = value2bits(value, self.resolution)
278
+ for i, b in enumerate(bits):
279
+ probabilities[i] += probability * b
280
+ lo2hi_probability = sum(p * (1 - p) for p in probabilities)
281
+ flip_flops_e *= lo2hi_probability
282
+ assert flip_flops_e >= 0
283
+
284
+ self.logger.info(f"Flip-flops consumed {flip_flops_e}J")
285
+ return energy + self._get_controller_energy() + cap_e + flip_flops_e
286
+
287
+ def _get_controller_energy(self):
288
+ # 0.08pJ/bit at 22nm 1.0V
289
+ return (self.voltage * self.tech_node) ** 2 * self.resolution * 8e-8
290
+
291
+ @action
292
+ def convert(self):
293
+ """
294
+ Returns the energy and latency to convert the input value to an analog voltage.
295
+
296
+ Returns
297
+ -------
298
+ float: Energy in Joules float: Latency in seconds
299
+ """
300
+ min_latency = self._get_latency(
301
+ load_capacitance=self.load_capacitance, load_resistance=self.load_resistance
302
+ )
303
+ energy = self._convert_energy(latency=min_latency)
304
+ controller_energy = self._get_controller_energy()
305
+ self.logger.info(
306
+ f"{self._kind} DAC consumes {energy}J over{min_latency} seconds. "
307
+ f"Resistors consume {energy - controller_energy}J, "
308
+ f"Controller energy: {controller_energy}J"
309
+ )
310
+ return energy, min_latency
311
+
312
+ @action
313
+ def read(self):
314
+ """
315
+ Returns the energy and latency to convert the input value to an analog voltage.
316
+
317
+ Returns
318
+ -------
319
+ float: Energy in Joules float: Latency in seconds
320
+ """
321
+ return self.convert()
322
+
323
+
324
+ class C2CLadderDAC(_X2XLadderDAC):
325
+ """
326
+ C-2C ladder DAC.
327
+
328
+ R-2R ladder and C-2C ladder DACs model those described in the paper: A Charge Domain
329
+ SRAM Compute-in-Memory Macro With C-2C Ladder-Based 8b MAC Unit in 22-nm FinFET
330
+ Process for Edge Inference Wang, Hechen and Liu, Renzhi and Dorrance, Richard and
331
+ Dasalukunte, Deepak and Lake, Dan and Carlton, Brent 10.1109/JSSC.2022.3232601
332
+
333
+ Parameters
334
+ ----------
335
+ resolution: int
336
+ The resolution of the DAC.
337
+ voltage: float
338
+ The voltage of the DAC.
339
+ unit_resistance: float
340
+ The unit resistance of the DAC.
341
+ tech_node: int
342
+ The tech node of the DAC.
343
+ cycle_period: float, optional
344
+ The cycle period of the clock driving the DAC, and also the minimum time the DAC
345
+ must hold any output values (longer if it takes longer to settle). Defaults to
346
+ 1e-9.
347
+ hist: List[float], optional
348
+ The histogram of the DAC's input values. Defaults to None. This should be a list
349
+ of probabilities, the first item being the probability of the minimum value, the
350
+ last item being the probability of the maximum value, and the rest being the
351
+ probabilities of the intermediate values.
352
+ load_resistance: float
353
+ The load resistance on the DAC's output. Defaults to 0.
354
+ load_capacitance: float
355
+ The load capacitance on the DAC's output. Defaults to 0.
356
+
357
+ Attributes
358
+ ----------
359
+ resolution: int
360
+ The resolution of the DAC.
361
+ voltage: float
362
+ The voltage of the DAC.
363
+ unit_capacitance: float
364
+ The unit capacitance of the DAC.
365
+ tech_node: int
366
+ The tech node of the DAC.
367
+ cycle_period: float, optional
368
+ The cycle period of the clock driving the DAC, and also the minimum time the DAC
369
+ must hold any output values (longer if it takes longer to settle). Defaults to
370
+ 1e-9.
371
+ hist: List[float], optional
372
+ The histogram of the DAC's input values. Defaults to None. This should be a list
373
+ of probabilities, the first item being the probability of the minimum value, the
374
+ last item being the probability of the maximum value, and the rest being the
375
+ probabilities of the intermediate values.
376
+ load_resistance: float
377
+ The load resistance on the DAC's output. Defaults to 0.
378
+ load_capacitance: float
379
+ The load capacitance on the DAC's output. Defaults to 0.
380
+
381
+ """
382
+
383
+ component_name: list[str] = ["C2CLadderDAC", "C2CDAC"]
384
+
385
+ def __init__(
386
+ self,
387
+ resolution: int,
388
+ voltage: float,
389
+ unit_capacitance: float,
390
+ tech_node: int,
391
+ hist: List[float] = None,
392
+ capacitors_consume_area: bool = True,
393
+ cycle_period: float = 1e-9,
394
+ load_capacitance: float = 0,
395
+ load_resistance: float = 0,
396
+ ):
397
+ self.unit_capacitance = unit_capacitance
398
+
399
+ self._unit_cap = Capacitor(
400
+ voltage=self.voltage, capacitance=unit_capacitance, tech_node=tech_node
401
+ )
402
+ self._unit2_cap = Capacitor(
403
+ voltage=self.voltage, capacitance=unit_capacitance * 2, tech_node=tech_node
404
+ )
405
+ if not capacitors_consume_area:
406
+ self._unit_cap.area_scale *= 0
407
+ self._unit2_cap.area_scale *= 0
408
+
409
+ super().__init__(
410
+ resolution=resolution,
411
+ voltage=voltage,
412
+ unit_x=unit_capacitance,
413
+ tech_node=tech_node,
414
+ hist=hist,
415
+ load_capacitance=unit_capacitance * 2 + load_capacitance,
416
+ area=self._unit_cap.area + self._unit2_cap.area,
417
+ leak_power=self._unit_cap.leak_power + self._unit2_cap.leak_power,
418
+ kind="C2C",
419
+ load_resistance=load_resistance,
420
+ cycle_period=cycle_period,
421
+ )
422
+ self.scale(
423
+ "resolution",
424
+ resolution,
425
+ 1,
426
+ area_scale_function=linear,
427
+ leak_power_scale_function=linear,
428
+ )
429
+
430
+
431
+ class R2RLadderDAC(_X2XLadderDAC):
432
+ """
433
+ R-2R ladder DAC.
434
+
435
+ R-2R ladder and C-2C ladder DACs model those described in the paper: A Charge Domain
436
+ SRAM Compute-in-Memory Macro With C-2C Ladder-Based 8b MAC Unit in 22-nm FinFET
437
+ Process for Edge Inference Wang, Hechen and Liu, Renzhi and Dorrance, Richard and
438
+ Dasalukunte, Deepak and Lake, Dan and Carlton, Brent 10.1109/JSSC.2022.3232601
439
+
440
+ Parameters
441
+ ----------
442
+ resolution: int
443
+ The resolution of the DAC.
444
+ voltage: float
445
+ The voltage of the DAC.
446
+ unit_resistance: float
447
+ The unit resistance of the DAC.
448
+ tech_node: int
449
+ The tech node of the DAC.
450
+ cycle_period: float, optional
451
+ The cycle period of the clock driving the DAC, and also the minimum time the DAC
452
+ must hold any output values (longer if it takes longer to settle). Defaults to
453
+ 1e-9.
454
+ hist: List[float], optional
455
+ The histogram of the DAC's input values. Defaults to None. This should be a list
456
+ of probabilities, the first item being the probability of the minimum value, the
457
+ last item being the probability of the maximum value, and the rest being the
458
+ probabilities of the intermediate values.
459
+ load_resistance: float
460
+ The load resistance on the DAC's output. Defaults to 0.
461
+ load_capacitance: float
462
+ The load capacitance on the DAC's output. Defaults to 0.
463
+
464
+ Attributes
465
+ ----------
466
+ resolution: int
467
+ The resolution of the DAC.
468
+ voltage: float
469
+ The voltage of the DAC.
470
+ unit_resistance: float
471
+ The unit resistance of the DAC.
472
+ tech_node: int
473
+ The tech node of the DAC.
474
+ cycle_period: float, optional
475
+ The cycle period of the clock driving the DAC, and also the minimum time the DAC
476
+ must hold any output values (longer if it takes longer to settle). Defaults to
477
+ 1e-9.
478
+ hist: List[float], optional
479
+ The histogram of the DAC's input values. Defaults to None. This should be a list
480
+ of probabilities, the first item being the probability of the minimum value, the
481
+ last item being the probability of the maximum value, and the rest being the
482
+ probabilities of the intermediate values.
483
+ load_resistance: float
484
+ The load resistance on the DAC's output. Defaults to 0.
485
+ load_capacitance: float
486
+ The load capacitance on the DAC's output. Defaults to 0.
487
+ """
488
+
489
+ component_name: list[str] = ["R2RLadderDAC", "R2RDAC"]
490
+
491
+ def __init__(
492
+ self,
493
+ resolution: int,
494
+ voltage: float,
495
+ unit_resistance: float,
496
+ tech_node: int,
497
+ cycle_period: float = 1e-9,
498
+ hist: List[float] = None,
499
+ load_resistance: float = 0,
500
+ load_capacitance: float = 0,
501
+ ):
502
+ self.unit_resistance = unit_resistance
503
+ self._m2_chip_area_per_ohm = 2.35e-15 * (tech_node / 22e-9) ** 2
504
+
505
+ super().__init__(
506
+ resolution=resolution,
507
+ voltage=voltage,
508
+ unit_x=1 / unit_resistance / 2,
509
+ tech_node=tech_node,
510
+ hist=hist,
511
+ area=self._m2_chip_area_per_ohm * resolution * unit_resistance * 3,
512
+ leak_power=0,
513
+ cycle_period=cycle_period,
514
+ kind="R2R",
515
+ load_resistance=load_resistance,
516
+ load_capacitance=load_capacitance,
517
+ )
518
+
519
+
520
+ class DualSidedR2RLadderDAC(R2RLadderDAC):
521
+ """
522
+ Dual-sided R-2R ladder DAC. This DAC has two sides, one for the positive half of the
523
+ voltage range and one for the negative half.
524
+
525
+ R-2R ladder and C-2C ladder DACs model those described in the paper: A Charge Domain
526
+ SRAM Compute-in-Memory Macro With C-2C Ladder-Based 8b MAC Unit in 22-nm FinFET
527
+ Process for Edge Inference Wang, Hechen and Liu, Renzhi and Dorrance, Richard and
528
+ Dasalukunte, Deepak and Lake, Dan and Carlton, Brent 10.1109/JSSC.2022.3232601
529
+
530
+ Parameters
531
+ ----------
532
+ resolution: int
533
+ The resolution of the DAC.
534
+ voltage: float
535
+ The voltage of the DAC.
536
+ unit_resistance: float
537
+ The unit resistance of the DAC.
538
+ tech_node: int
539
+ The tech node of the DAC.
540
+ cycle_period: float, optional
541
+ The cycle period of the clock driving the DAC, and also the minimum time the DAC
542
+ must hold any output values (longer if it takes longer to settle). Defaults to
543
+ 1e-9.
544
+ hist: List[float], optional
545
+ The histogram of the DAC's input values. Defaults to None. This should be a list
546
+ of probabilities, the first item being the probability of the minimum value, the
547
+ last item being the probability of the maximum value, and the rest being the
548
+ probabilities of the intermediate values.
549
+ load_resistance: float
550
+ The load resistance on the DAC's output. Defaults to 0.
551
+ load_capacitance: float
552
+ The load capacitance on the DAC's output. Defaults to 0.
553
+
554
+ Attributes
555
+ ----------
556
+ resolution: int
557
+ The resolution of the DAC.
558
+ voltage: float
559
+ The voltage of the DAC.
560
+ unit_resistance: float
561
+ The unit resistance of the DAC.
562
+ tech_node: int
563
+ The tech node of the DAC.
564
+ cycle_period: float, optional
565
+ The cycle period of the clock driving the DAC, and also the minimum time the DAC
566
+ must hold any output values (longer if it takes longer to settle). Defaults to
567
+ 1e-9.
568
+ hist: List[float], optional
569
+ The histogram of the DAC's input values. Defaults to None. This should be a list
570
+ of probabilities, the first item being the probability of the minimum value, the
571
+ last item being the probability of the maximum value, and the rest being the
572
+ probabilities of the intermediate values.
573
+ load_resistance: float
574
+ The load resistance on the DAC's output. Defaults to 0.
575
+ load_capacitance: float
576
+ The load capacitance on the DAC's output. Defaults to 0.
577
+ """
578
+
579
+ component_name: list[str] = ["DualSidedR2RLadderDAC", "DualSidedR2RDAC"]
580
+
581
+ def __init__(
582
+ self,
583
+ resolution: int,
584
+ voltage: float,
585
+ unit_resistance: float,
586
+ tech_node: int,
587
+ cycle_period: float = 1e-9,
588
+ hist: List[float] = None,
589
+ load_resistance: float = 0,
590
+ load_capacitance: float = 0,
591
+ ):
592
+ super().__init__(
593
+ resolution=resolution,
594
+ voltage=voltage,
595
+ unit_resistance=unit_resistance,
596
+ tech_node=tech_node,
597
+ cycle_period=cycle_period,
598
+ hist=hist,
599
+ load_resistance=load_resistance,
600
+ load_capacitance=load_capacitance,
601
+ )
602
+ self.leak_power_scale *= 2 # Two sides
603
+ self.area_scale *= 2 # Two sides
604
+ self.energy_scale *= 2 # Two sides
605
+ # Voltage is still the full range because we're pulling from a VDD supply