|
| 1 | +# @ARTICLE{9373949, |
| 2 | +# author={Kim, Hyunjoon and Yoo, Taegeun and Kim, Tony Tae-Hyoung and Kim, Bongjin}, |
| 3 | +# journal={IEEE Journal of Solid-State Circuits}, |
| 4 | +# title={Colonnade: A Reconfigurable SRAM-Based Digital Bit-Serial Compute-In-Memory Macro for Processing Neural Networks}, |
| 5 | +# year={2021}, |
| 6 | +# volume={56}, |
| 7 | +# number={7}, |
| 8 | +# pages={2221-2233}, |
| 9 | +# doi={10.1109/JSSC.2021.3061508}} |
| 10 | + |
| 11 | +{{include_text('_include.yaml')}} |
| 12 | +{{add_to_path('./memory_cells')}} |
| 13 | + |
| 14 | +arch: |
| 15 | + # =================================================================================== |
| 16 | + # Architecture-specific variables. |
| 17 | + # =================================================================================== |
| 18 | + variables: |
| 19 | + # Inherit these from _include.yaml. Many definitions are included there. |
| 20 | + <<: *variables_global |
| 21 | + |
| 22 | + # Encoding-dependent parameters --------------------------------------------------- |
| 23 | + encoded_input_bits: input_bits |
| 24 | + encoded_weight_bits: weight_bits + ceil(log2(array_parallel_inputs)) |
| 25 | + encoded_output_bits: output_bits |
| 26 | + |
| 27 | + input_encoding_func: offset_encode_hist |
| 28 | + weight_encoding_func: offset_encode_hist |
| 29 | + |
| 30 | + # For accuracy model. Can in-array accumulation include signed values? Signed |
| 31 | + # accumulation not compatible with offset encoding (since offset encoding makes |
| 32 | + # values non-negative) |
| 33 | + signed_sum_across_inputs: False |
| 34 | + signed_sum_across_weights: False |
| 35 | + |
| 36 | + # Architecture & CiM Array Structure ---------------------------------------------- |
| 37 | + # DEFINITIONS: |
| 38 | + # - Cell: Smallest structure capable of storing memory. Note that a cell may store |
| 39 | + # more than one bit. For example, a cell consisting of a RRAM device may store >1 |
| 40 | + # bits, while a cell consisting of an SRAM bitcell may store only 1 bit. |
| 41 | + # - CiM Unit: Smallest structure capable of computing an analog MAC |
| 42 | + # - CiM Unit Width Cells: Number of CiM unit cells that are accessed as one. These |
| 43 | + # cells receive one analog input and compute one analog MAC per timestep. |
| 44 | + # - CiM Unit Depth Cells: Number of independent groups of "CiM Unit Width" cells |
| 45 | + # that form a CiM unit. Each of these groups is indepently addressible and |
| 46 | + # operates in must be activated in a different timestep than the others. |
| 47 | + cim_unit_width_cells: 1 |
| 48 | + cim_unit_depth_cells: 1 |
| 49 | + bits_per_cell: 1 |
| 50 | + |
| 51 | + # Data Converters ----------------------------------------------------------------- |
| 52 | + adc_resolution: 1 |
| 53 | + voltage_dac_resolution: 1 |
| 54 | + temporal_dac_resolution: 1 |
| 55 | + n_adc_per_bank: 1 |
| 56 | + |
| 57 | + # Hardware ------------------------------------------------------------------------ |
| 58 | + cycle_period: base_latency * voltage_latency_scale * latency_pipeline_scale |
| 59 | + read_pulse_width: cycle_period |
| 60 | + |
| 61 | + n_regs_per_col: 16 |
| 62 | + n_rows_per_reg: 8 |
| 63 | + |
| 64 | + # True = 100 MHz fixed clock for energy tests, False = variable frequency |
| 65 | + # based on critical path |
| 66 | + force_100mhz: False |
| 67 | + bit_pipelined: True |
| 68 | + n_adders_critical_path: encoded_weight_bits + n_rows_per_reg + 2 |
| 69 | + critical_path_latency: n_adders_critical_path * 0.4e-9 |
| 70 | + |
| 71 | + base_latency: 10e-9 if force_100mhz else critical_path_latency |
| 72 | + latency_pipeline_scale: 1 if bit_pipelined else (n_regs_per_col + 1) |
| 73 | + |
| 74 | + extra_attributes_for_all_component_models: |
| 75 | + <<: *cim_component_attributes |
| 76 | + tech_node: tech_node |
| 77 | + cycle_period: cycle_period |
| 78 | + |
| 79 | + # =================================================================================== |
| 80 | + # Architecture |
| 81 | + # =================================================================================== |
| 82 | + nodes: |
| 83 | + # This memory catches sliding windows that may be sent spatially in the array. E.g., |
| 84 | + # convolution steps spatially unrolled onto columns with overlapping windows. |
| 85 | + - !Memory |
| 86 | + name: DummyRowDriverMemory |
| 87 | + component_class: Dummy |
| 88 | + # Size = one input value per row (max number of inputs we can do in parallel) |
| 89 | + size: input.bits_per_value * array_parallel_inputs |
| 90 | + # no_resend_to_below prevents reuse across temporal iterations because this isn't |
| 91 | + # really a memory. |
| 92 | + tensors: {keep: input, no_resend_to_below: input} |
| 93 | + |
| 94 | + # Row drivers feed inputs onto the rows of the array. |
| 95 | + - !Toll |
| 96 | + name: RowDrivers |
| 97 | + <<: [*input_sliced, *one_action_per_value] |
| 98 | + tensors: {keep: input} |
| 99 | + direction: down |
| 100 | + component_class: ArrayRowDrivers |
| 101 | + area_scale: row_col_drivers_area_scale |
| 102 | + |
| 103 | + # The input ports of the CiM logic are modeled here, representing the energy to drive |
| 104 | + # all of the adders that will be switching for each input bit. We put this logic up |
| 105 | + # here, not by the CiM unit, because the input bits fan out to all columns regardless |
| 106 | + # of if the columns are used, so we charge the full energy here for each input bit |
| 107 | + # entering the array. |
| 108 | + - !Toll |
| 109 | + name: DigitalLogicInputPorts |
| 110 | + <<: [*input_sliced, *one_action_per_value] |
| 111 | + tensors: {keep: input} |
| 112 | + direction: down |
| 113 | + component_class: ColonnadeCimLogicInputPort |
| 114 | + extra_attributes_for_component_model: |
| 115 | + n_instances: array_bitlines |
| 116 | + switching_activity: (average_input_value * (1 - average_input_value)) ** 0.5 |
| 117 | + voltage: voltage |
| 118 | + voltage_energy_scale: voltage_energy_scale |
| 119 | + |
| 120 | + # A bitline decoder is used to select array columns. |
| 121 | + - !Toll |
| 122 | + name: ColumnDrivers |
| 123 | + <<: [*both_sliced, *one_action_per_value] |
| 124 | + tensors: {keep: output} |
| 125 | + direction: up |
| 126 | + component_class: ArrayColumnDrivers |
| 127 | + area_scale: row_col_drivers_area_scale |
| 128 | + # Cols active at once = 1 to create a full decoder. We use 1 / |
| 129 | + # array_parallel_outputs scaling because we actually can accumulate outputs in |
| 130 | + # parallel. |
| 131 | + latency_scale: 1 / array_parallel_outputs |
| 132 | + extra_attributes_for_component_model: {cols_active_at_once: 1} |
| 133 | + |
| 134 | + # Weight drivers write weights. |
| 135 | + - !Toll |
| 136 | + name: WeightDrivers |
| 137 | + tensors: {keep: weight & Above} |
| 138 | + direction: down |
| 139 | + component_class: ArrayRowDrivers |
| 140 | + bits_per_action: average_weight_bits_per_slice |
| 141 | + area_scale: row_col_drivers_area_scale |
| 142 | + extra_attributes_for_component_model: {<<: *weight_drivers_attributes} |
| 143 | + |
| 144 | + # Each column stores a different weight slices. Columns share inputs. |
| 145 | + - !Container |
| 146 | + name: Column |
| 147 | + spatial: |
| 148 | + # ARRAY_COLUMNS suffix used for used for counting the array size |
| 149 | + - name: column_ARRAY_COLUMNS |
| 150 | + fanout: 128 |
| 151 | + # Note: may_reuse here and not reuse because we may have columns that reuse some, |
| 152 | + # but not all, input values, so we want reuse to be optional and only for inputs. |
| 153 | + may_reuse: input |
| 154 | + min_usage: 1 |
| 155 | + usage_scale: n_weight_slices |
| 156 | + |
| 157 | + # Column bandwidth limiter to limit write speed (only one value can be written per |
| 158 | + # column per cycle) |
| 159 | + - !Toll |
| 160 | + name: ColumnBandwidthLimiter |
| 161 | + tensors: {keep: (weight | output) & Above} |
| 162 | + direction: {Outputs: up, ~Outputs: down} |
| 163 | + component_class: Dummy |
| 164 | + bits_per_value: |
| 165 | + weight: 1 |
| 166 | + output: n_input_slices |
| 167 | + actions: [{name: read, latency: cycle_period}] |
| 168 | + |
| 169 | + # Each group of rows has a register for pipelining. Rows share outputs. |
| 170 | + - !Container |
| 171 | + name: RowGroup |
| 172 | + spatial: |
| 173 | + - name: group_ARRAY_ROWS |
| 174 | + fanout: n_regs_per_col |
| 175 | + reuse: output |
| 176 | + min_usage: 1 |
| 177 | + |
| 178 | + # Register stores partially-accumulated outputs. They are used for pipelining the |
| 179 | + # design to run the array at a higher clock frequency. |
| 180 | + - !Toll |
| 181 | + name: Register |
| 182 | + <<: [*both_sliced, *one_action_per_value] |
| 183 | + tensors: {keep: output} |
| 184 | + direction: up |
| 185 | + component_class: ColonnadeRegister |
| 186 | + extra_attributes_for_component_model: |
| 187 | + width: encoded_output_bits |
| 188 | + voltage_energy_scale: voltage_energy_scale |
| 189 | + |
| 190 | + # Each row receives a different input slice. Rows share outputs. |
| 191 | + - !Container |
| 192 | + name: Row |
| 193 | + spatial: |
| 194 | + # ARRAY_ROWS suffix used for used for counting the array size |
| 195 | + - name: row_ARRAY_ROWS |
| 196 | + fanout: n_rows_per_reg |
| 197 | + reuse: output |
| 198 | + min_usage: 1 |
| 199 | + |
| 200 | + # CiM units stores weights and computes MACs. Each CiM unit stores up to |
| 201 | + # cim_unit_depth_cells indepedently-addressable weight slices, with up to |
| 202 | + # cim_unit_width_cells * cim_unit_depth_cells in a weight slice. Only one weight slice |
| 203 | + # can be used at a time. |
| 204 | + - !Memory |
| 205 | + name: CimUnit |
| 206 | + # Computes with one input slice * weight slice at a time. Computations are |
| 207 | + # parallelized across weight slices, and input slices are processed sequentially. |
| 208 | + <<: [*both_sliced_weight_slices_parallelized, *one_action_per_value] |
| 209 | + tensors: |
| 210 | + keep: weight |
| 211 | + # Write weights at most once |
| 212 | + no_refetch_from_above: weight |
| 213 | + # Can store values at this level longer than levels above, which lets the mapping |
| 214 | + # be maximally-weight-stationary (CimUnit weight storage node as high as |
| 215 | + # possible). |
| 216 | + force_memory_hierarchy_order: False |
| 217 | + # Each instance of this component represents n_weight_slices weight slices, meaning |
| 218 | + # that it holds a full weight |
| 219 | + size: cim_unit_width_cells * cim_unit_depth_cells * bits_per_cell |
| 220 | + component_class: MemoryCell |
| 221 | + # CimUnits represent cim_unit_width_cells * cim_unit_depth_cells memory cells tied |
| 222 | + # together into one weight |
| 223 | + extra_attributes_for_component_model: |
| 224 | + n_instances: cim_unit_width_cells * cim_unit_depth_cells |
| 225 | + |
| 226 | + # CiM logic (adder switching) performs MACs in the memory array. In Colonnade they |
| 227 | + # include a XOR gate to perform the multiply and an adder that sums values from this |
| 228 | + # XOR gate, the previous row, and the carry from the previous column. |
| 229 | + - !Toll |
| 230 | + name: CimLogic |
| 231 | + <<: [*both_sliced_weight_slices_parallelized, *one_action_per_value] |
| 232 | + tensors: {keep: weight} |
| 233 | + direction: down |
| 234 | + component_class: ColonnadeCimLogic |
| 235 | + extra_attributes_for_component_model: |
| 236 | + width: cim_unit_width_cells * bits_per_cell |
| 237 | + p_switch: (average_input_value * (1 - average_input_value)) ** 0.5 |
| 238 | + switching_activity: n_adders_critical_path * p_switch |
| 239 | + voltage_energy_scale: voltage_energy_scale |
| 240 | + |
| 241 | + # We account for compute energy and latency in other components, so just use a dummy |
| 242 | + # compute here. |
| 243 | + - !Compute |
| 244 | + name: FreeCompute |
| 245 | + component_class: Dummy |
| 246 | + enabled: len(All) == 3 |
| 247 | + |
| 248 | + |
| 249 | +# These variables pertain to the workload, microarch, and circuits. They should be |
| 250 | +# matched between architectures when comparing for a fair comparison. |
| 251 | +variables: |
| 252 | + inputs_hist: [1, 1, 1, 1, 1, 1, 1] |
| 253 | + weights_hist: [1, 1, 1, 1, 1, 1, 1] |
| 254 | + outputs_hist: inputs_hist |
| 255 | + |
| 256 | + ## Microarch ------------------------------------------------------------------------ |
| 257 | + supported_input_bits: 16 |
| 258 | + supported_weight_bits: 16 |
| 259 | + supported_output_bits: 24 |
| 260 | + min_supported_input_bits: 1 |
| 261 | + min_supported_weight_bits: 1 |
| 262 | + min_supported_output_bits: 1 |
| 263 | + |
| 264 | + # Circuits -------------------------------------------------------------------------- |
| 265 | + voltage: 0.8 |
| 266 | + tech_node: 65e-9 # 65nm |
| 267 | + cell_config: "{{find_path('sram_colonnade_jssc_2021.yaml')}}" |
| 268 | + voltage_energy_scale: (voltage / 0.8) ** 2 |
| 269 | + voltage_latency_scale: 0.8 / voltage |
| 270 | + |
| 271 | + # Calibration ----------------------------------------------------------------------- |
| 272 | + adc_energy_scale: 1 * voltage_energy_scale |
| 273 | + adc_area_scale: 1 |
| 274 | + row_col_drivers_area_scale: 1 |
| 275 | + |
| 276 | + |
| 277 | +# ===================================================================================== |
| 278 | +# Default Workload |
| 279 | +# ===================================================================================== |
| 280 | +# This workload is sized to get peak throughput & energy efficiency. |
| 281 | +# 128 columns × (16 regs × 8 rows/reg) = 128 × 128 rows |
| 282 | +workload: |
| 283 | + rank_sizes: |
| 284 | + M: 1 |
| 285 | + N: 128 |
| 286 | + K: 128 |
| 287 | + |
| 288 | + einsums: |
| 289 | + - name: Matmul |
| 290 | + tensor_accesses: |
| 291 | + - {name: input, projection: [m, k], bits_per_value: 1} |
| 292 | + - {name: weight, projection: [k, n], bits_per_value: 1} |
| 293 | + - {name: output, projection: [m, n], output: True, bits_per_value: 1} |
| 294 | + |
| 295 | +renames: {} |
0 commit comments