# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# ## BitFusion Tutorial
#
# ***Bit Fusion: Bit-Level Dynamically Composable Architecture for Accelerating Deep Neural Networks*** **[\[arXiv\]](https://arxiv.org/abs/1712.01507)** **[\[Github\]](https://github.com/hsharma35/bitfusion)**

# ### Motivation for BitFusion (Why do you need it?)
# As its name suggests, BitFusion provides the dedicated hardware supports for dynamic bit-level allocation in deep neural networks (DNN). Specifically, 1) It is a bit-flexible accelerator that constitutes an array of bit-level processing elements that dynamically fuse to match the bitwidth of individual DNN layers. 2) It has a lot of benefits and surpass the state-of-the-art accelerators: Eyeriss and Stripes. I will use this tutorial to introduce the concrete usage of BitFusion.

# #### Original Codebase

# download the source code of simulator
# !git clone https://github.com/hsharma35/bitfusion.git && cd bitfusion

# It seems there is a guide to show the usage, but that is only a showcase (i.e., plot code) without a corresponding critial data file.<br>
# *check the issue here: https://github.com/hsharma35/bitfusion/issues/2*

# #### Prerequisites

# !pip install -r requirements.txt
# !pip install configparser

# !git clone https://github.com/hsharma35/dnnweaver2.git
# !mv ./dnnweaver2 ./dnnweaver2-repo
# !cp -r ./dnnweaver2-repo/dnnweaver2 dnnweaver2

# #### Python wrapper to generate SRAM sweeps with different capacity/#banks/widths

# Prerequisites: Clone and compile cacti-7
# !cd sram && git clone https://github.com/HewlettPackard/cacti.git

# or you can use the released version
# ! cd sram && wget https://github.com/HewlettPackard/cacti/archive/v6.5.0.tar.gz

# !cd sram && tar -xvzf v6.5.0.tar.gz

# compile the project
# !cd ./sram/cacti && make

# To test the installation of cacti-7 and the python wrapper: 
# !cd sram && python cacti_sweep.py

# #### Last Step: 
# * Replace the sweep.py in `./src/sweep/sweep.py` to the new file (https://drive.google.com/file/d/13yARyK7nFevNF15689Swyh6LgE7M6Qff/view?usp=sharing)
# * Replace the stats.py in `./src/simulator/stats.py` to the new file (https://drive.google.com/file/d/1Dlb6r1dfrbXYMaP4fS0Y-2_wpGPbHUH4/view?usp=sharing)

# ### Then you can run simulation

# +
import pandas
import configparser
import os
import numpy as np
from graph_plot.barchart import BarChart

import matplotlib

import warnings
warnings.filterwarnings('ignore')

import dnnweaver2

import src.benchmarks.benchmarks as benchmarks
from src.simulator.stats import Stats
from src.simulator.simulator import Simulator
from src.sweep.sweep import SimulatorSweep, check_pandas_or_run
from src.utils.utils import *
from src.optimizer.optimizer import optimize_for_order, get_stats_fast

# +
## use a batch size of 16
batch_size = 128

results_dir = './results'
if not os.path.exists(results_dir):
    os.makedirs(results_dir)

fig_dir = './fig'
if not os.path.exists(fig_dir):
    os.makedirs(fig_dir)

## establish systolic arrray
from pandas import DataFrame
# -

# ## ***Below is the missed file that I recovered from showcase***

# +
data = {'Max Precision (bits)': [8, 8],
        'Min Precision (bits)': [2, 2],
        'M': [32, 4],
        'N': [16, 4],
        'Area (um^2)': [1, 1],
        'Dynamic Power (nW)': [1, 1],
        'Frequency': [1, 1],
        'Leakage Power (nW)': [1, 1]
        }

df = DataFrame(data, columns=list(data.keys()))
export_csv = df.to_csv (r'./results/systolic_array_synth.csv', index = None, header=True)
print (df)

# +
config_file = 'bf_e_conf.ini'
# config_file = 'conf.ini'

# Create simulator object
verbose = False
bf_e_sim = Simulator(config_file, verbose)
bf_e_energy_costs = bf_e_sim.get_energy_cost()
print(bf_e_sim)

energy_tuple = bf_e_energy_costs
print('')
print('*'*50)
print(energy_tuple)
# -

# ### ***You can define your own network (modify the `./src/benchmarks/benchmarks.py`)***
# below I use show the default several networks.

# +
# bench_list = benchmarks.FracTrain_benchlist
# bench_list = benchmarks.L2A_benchlist
bench_list = benchmarks.benchlist

sim_sweep_columns = ['N', 'M',
        'Max Precision (bits)', 'Min Precision (bits)',
        'Network', 'Layer',
        'Cycles', 'Memory wait cycles',
        'WBUF Read', 'WBUF Write',
        'OBUF Read', 'OBUF Write',
        'IBUF Read', 'IBUF Write',
        'DRAM Read', 'DRAM Write',
        'Bandwidth (bits/cycle)',
        'WBUF Size (bits)', 'OBUF Size (bits)', 'IBUF Size (bits)',
        'Batch size']

# bf_e_sim_sweep_csv = os.path.join(results_dir, 'bitfusion-eyeriss-sim-sweep.csv')
# if os.path.exists(bf_e_sim_sweep_csv):
#     bf_e_sim_sweep_df = pandas.read_csv(bf_e_sim_sweep_csv)
# else:
#     bf_e_sim_sweep_df = pandas.DataFrame(columns=sim_sweep_columns)
bf_e_sim_sweep_csv = os.path.join(results_dir, 'trial.csv')
if os.path.exists(bf_e_sim_sweep_csv):
    os.remove(bf_e_sim_sweep_csv)
bf_e_sim_sweep_df = pandas.DataFrame(columns=sim_sweep_columns)
print('Got BitFusion Eyeriss, Numbers')

bf_e_results = check_pandas_or_run(bf_e_sim, bf_e_sim_sweep_df, bf_e_sim_sweep_csv, list_bench=bench_list, batch_size=batch_size, config_file='./conf.ini')
bf_e_results = bf_e_results.groupby('Network',as_index=False).agg(np.sum)
export_csv = bf_e_results.to_csv (r'./results/trial_stat.csv', index = None, header=True)
area_stats = bf_e_sim.get_area()
# -

from src.simulator.stats import Stats
def df_to_stats(df):
    stats = Stats()
    stats.total_cycles = float(df['Cycles'])
    stats.mem_stall_cycles = float(df['Memory wait cycles'])
    stats.reads['act'] = float(df['IBUF Read'])
    stats.reads['out'] = float(df['OBUF Read'])
    stats.reads['wgt'] = float(df['WBUF Read'])
    stats.reads['dram'] = float(df['DRAM Read'])
    stats.writes['act'] = float(df['IBUF Write'])
    stats.writes['out'] = float(df['OBUF Write'])
    stats.writes['wgt'] = float(df['WBUF Write'])
    stats.writes['dram'] = float(df['DRAM Write'])
    return stats


# +
print('BitFusion-Eyeriss comparison')
eyeriss_area = 3.5*3.5*45*45/65./65.
print('Area budget = {}'.format(eyeriss_area))


print(area_stats)
if abs(sum(area_stats)-eyeriss_area)/eyeriss_area > 0.1:
    print('Warning: BitFusion Area is outside 10% of eyeriss')
print('total_area = {}, budget = {}'.format(sum(area_stats), eyeriss_area))
bf_e_area = sum(area_stats)

baseline_data = []
for bench in bench_list:
    lookup_dict = {'Benchmark': bench}

    # eyeriss_cycles = float(lookup_pandas_dataframe(eyeriss_data_bench, lookup_dict)['time(ms)'])
    # eyeriss_time = eyeriss_cycles / 500.e3 / 16
    # eyeriss_energy = get_eyeriss_energy(lookup_pandas_dataframe(eyeriss_data_bench, lookup_dict))
    # eyeriss_power = eyeriss_energy / eyeriss_time * 1.e-9

    # eyeriss_speedup = eyeriss_time / eyeriss_time
    # eyeriss_energy_efficiency = eyeriss_energy / eyeriss_energy

    # eyeriss_ppa = eyeriss_speedup / eyeriss_area / (eyeriss_speedup / eyeriss_area)
    # eyeriss_ppw = eyeriss_speedup / eyeriss_power / (eyeriss_speedup / eyeriss_power)

    bf_e_stats = df_to_stats(bf_e_results.loc[bf_e_results['Network'] == bench])
    bf_e_cycles = bf_e_stats.total_cycles * (batch_size / 16.)
    bf_e_time = bf_e_cycles / 500.e3 #/ 16
    cc_energy, mem_energy = bf_e_stats.get_energy(bf_e_sim.get_energy_cost())
    cc_energy = cc_energy * (batch_size / 16.)
    mem_energy = mem_energy * (batch_size / 16.)
    print('cc_energy: {}, mem_energy: {}'.format(cc_energy*1.e-9, mem_energy*1.e-9))
    bf_e_energy = cc_energy + mem_energy
    bf_e_power = bf_e_energy / bf_e_time * 1.e-9

    # bf_e_speedup = eyeriss_time / bf_e_time
    # bf_e_energy_efficiency = eyeriss_energy / bf_e_energy

    # bf_e_ppa = bf_e_speedup / bf_e_area / (eyeriss_speedup / eyeriss_area)
    # bf_e_ppw = bf_e_speedup / bf_e_power / (eyeriss_speedup / eyeriss_power)

    # baseline_data.append(['Performance', bench, bf_e_speedup])
    # baseline_data.append(['Energy reduction', bench, bf_e_energy_efficiency])
    # baseline_data.append(['Performance-per-Watt', bench, bf_e_ppw])
    # baseline_data.append(['Performance-per-Area', bench, bf_e_ppa])

    print('*'*50)
    print('Benchmark: {}'.format(bench))
    # print('Eyeriss time: {} ms'.format(eyeriss_time))
    print('BitFusion time: {} ms'.format(bf_e_time))
    # print('Eyeriss power: {} mWatt'.format(eyeriss_power*1.e3*16))
    print('BitFusion power: {} mWatt'.format(bf_e_power*1.e3*16))
    # print('BitFusion energy: {} mJ'.format(bf_e_time*bf_e_power*1.e3*16 / 1.e3))
    print('BitFusion energy: {} J'.format(bf_e_energy*1.e-9))
    print('*'*50)
# -



StarcoderdataJupyterScriptsDedupFiltered