Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gemmini Data Collection feature #25

Open
wants to merge 27 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions bareMetalC/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,8 @@ tests = \
matrix_add \
resadd \
global_average \
gemmini_counter \
template \
# gemmini_counter \
# reset_counters \
# get_counters

tests_baremetal = $(tests:=-baremetal)

Expand All @@ -72,8 +70,6 @@ else
tests_pk = $(tests:=-pk)
endif

tests_pk =

BENCH_COMMON = $(abs_top_srcdir)/riscv-tests/benchmarks/common
GEMMINI_HEADERS = $(abs_top_srcdir)/include/gemmini.h $(abs_top_srcdir)/include/gemmini_params.h $(abs_top_srcdir)/include/gemmini_testutils.h

Expand All @@ -94,6 +90,7 @@ CFLAGS := $(CFLAGS) \
-I$(abs_top_srcdir) \
-I$(BENCH_COMMON) \
-DID_STRING=$(ID_STRING) \
-DPRINT_TILE=0 \

CFLAGS_PK := \
$(CFLAGS) \
Expand Down
6 changes: 3 additions & 3 deletions bareMetalC/tiled_matmul_ws.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ typedef elem_t ACC_T;
#endif

#ifndef BAREMETAL
#define MAT_DIM_I 12544 // 512
#define MAT_DIM_K 256 // 512
#define MAT_DIM_J 64 // 512
#define MAT_DIM_I 512
#define MAT_DIM_K 512
#define MAT_DIM_J 512
#else
#define MAT_DIM_I 64
#define MAT_DIM_K 64
Expand Down
52 changes: 16 additions & 36 deletions bareMetalC/tiled_matmul_ws_perf.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
#endif
#include "include/gemmini_testutils.h"

#define HEADS 1

#define ACTIVATION NO_ACTIVATION
// #define ACTIVATION SOFTMAX

#define NO_BIAS 0
#define REPEATING_BIAS 1
Expand All @@ -23,23 +20,15 @@

#ifndef BAREMETAL

#define MAT_DIM_I 128 // 128 // 256
#define MAT_DIM_K 512 // 64 // 256
#define MAT_DIM_J 512 // 128 // 256
#define MAT_DIM_I 128
#define MAT_DIM_K 512
#define MAT_DIM_J 256

#else

// #define MAT_DIM_I 128
// #define MAT_DIM_K 128
// #define MAT_DIM_J 128

#define MAT_DIM_I 512 // 256
#define MAT_DIM_K 512 // 256
#define MAT_DIM_J 32 // 256

// #define MAT_DIM_I 256
// #define MAT_DIM_K 512
// #define MAT_DIM_J 512
#define MAT_DIM_I 128
#define MAT_DIM_K 256
#define MAT_DIM_J 256

#endif

Expand All @@ -63,31 +52,25 @@ int main() {
}
#endif

printf("HEADS: %d\n", HEADS);
printf("MAT_DIM_I: %d\n", MAT_DIM_I);
printf("MAT_DIM_J: %d\n", MAT_DIM_J);
printf("MAT_DIM_K: %d\n", MAT_DIM_K);
printf("ACTIVATION: %d\n", ACTIVATION);

gemmini_flush(0);

#if A_TRANSPOSE==0
static elem_t full_A[HEADS][MAT_DIM_I][MAT_DIM_K] row_align(1);
static elem_t full_A[MAT_DIM_I][MAT_DIM_K] row_align(1);
#else
static elem_t full_A[HEADS][MAT_DIM_K][MAT_DIM_I] row_align(1);
static elem_t full_A[MAT_DIM_K][MAT_DIM_I] row_align(1);
#endif

#if B_TRANSPOSE==0
static elem_t full_B[HEADS][MAT_DIM_K][MAT_DIM_J] row_align(1);
static elem_t full_B[MAT_DIM_K][MAT_DIM_J] row_align(1);
#else
static elem_t full_B[HEADS][MAT_DIM_J][MAT_DIM_K] row_align(1);
static elem_t full_B[MAT_DIM_J][MAT_DIM_K] row_align(1);
#endif

static elem_t full_C[HEADS][MAT_DIM_I][MAT_DIM_J] row_align(1);
static acc_t full_D[HEADS][MAT_DIM_I][MAT_DIM_J] row_align_acc(1);
static elem_t full_C[MAT_DIM_I][MAT_DIM_J] row_align(1);
static acc_t full_D[MAT_DIM_I][MAT_DIM_J] row_align_acc(1);

static full_t gold_full[HEADS][MAT_DIM_I][MAT_DIM_J];
static elem_t gold[HEADS][MAT_DIM_I][MAT_DIM_J];
static full_t gold_full[MAT_DIM_I][MAT_DIM_J];
static elem_t gold[MAT_DIM_I][MAT_DIM_J];

counter_configure(0, RDMA_BYTES_REC);
counter_configure(1, WDMA_BYTES_SENT);
Expand All @@ -99,11 +82,8 @@ int main() {
printf("A_TRANSPOSE: %d, B_TRANSPOSE: %d\n", A_TRANSPOSE, B_TRANSPOSE);
uint64_t start = read_cycles();

for (int head = 0; head < HEADS; head++)
tiled_matmul_auto(MAT_DIM_I, MAT_DIM_J, MAT_DIM_K,
// (elem_t*)full_A, (elem_t*)full_B, NO_BIAS ? NULL : &full_D[0][0], (elem_t*)full_C,
(elem_t*)full_A[head], (elem_t*)full_B[head], NO_BIAS ? NULL : &full_D[head][0][0], (elem_t*)full_C[head],
// (elem_t*)full_A[0], (elem_t*)full_B[0], NO_BIAS ? NULL : &full_D[0][0][0], (elem_t*)full_C[0],
(elem_t*)full_A, (elem_t*)full_B, NO_BIAS ? NULL : &full_D[0][0], (elem_t*)full_C,
A_STRIDE, B_STRIDE, MAT_DIM_J, MAT_DIM_J,
MVIN_SCALE_IDENTITY, MVIN_SCALE_IDENTITY, MVIN_SCALE_IDENTITY,
ACTIVATION, ACC_SCALE_IDENTITY, 0, REPEATING_BIAS,
Expand All @@ -117,7 +97,7 @@ int main() {
uint64_t end = read_cycles();
printf("Cycles taken: %llu\n", end-start);

const uint64_t total_macs = HEADS * MAT_DIM_I * MAT_DIM_J * MAT_DIM_K;
const uint64_t total_macs = MAT_DIM_I * MAT_DIM_J * MAT_DIM_K;
const uint64_t ideal_cycles = total_macs / (DIM * DIM);
const uint64_t utilization = 100 * ideal_cycles / (end-start);
printf("Total macs: %llu\n", total_macs);
Expand Down
1 change: 1 addition & 0 deletions gemmini-data-collection/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
clean.sh
123 changes: 123 additions & 0 deletions gemmini-data-collection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Gemmini Data Collection

This directory contains scripts that enable large-scale, seamless data collection for various test benches applied on various configurations of Gemmini.
These scripts generally work by automatically generating _new_ C source files in the `bareMetalC/` directory, and running them in parallel for rapid data collection.

Most of the following documentation assumes the current working directory is `gemmini/software/gemmini-rocc-tests/gemmini-data-collection`.

## How to Use

### Step 1: Create Test Bench Template

* Make a generalized version of the test bench you want to simulate on Gemmini in the `templates` folder.

* For all values of the test bench you want to make variable, replace each usage of each value with its respective `%<KEYWORD_NAME>%`.

* If you just want to run matrix multiplications or convolutions, then you can skip this step as we already provide templates for these operations.

### Step 2: Specify Tests to Run

* Open `tests.py` in your preferred text editor, and make `GemminiTest` objects for each test you want to simulate (e.g. one test for a 512-by-512-by-512 matmul, or another test for a small convolution). You may refer to the existing examples in `tests.py`.

* The `GemminiTest` objects should have the following arguments:
* **Parameter 1**: an array of keywords used in the template
* **Parameter 2**: an array of values to replace the keywords specified in Parameter 1 (in respective order)
* **Parameter 3**: name of template you are using (must be a `C` file located in the `templates` folder)
* **Parameter 4**: name of output file (`C` file) you want generated in `bareMetalC` folder

### Step 3: Construct Gemmini Configurations

* This step is optional and is only necessary if you wish to collect simulation data for hardware configurations different than the Gemmini defaults (e.g. with different spatial array dimensions, or scratchpad sizes).

* Go to `gemmini/src/main/scala/gemmini/CustomConfigs.scala` and create your desired Gemmini configs

* Keep note of their names – you will need them for Step 4

### Step 4: Run Tests in Parallel

* Refer to descriptions of `gen_data.sh` and `config_gen_data.sh` in the section below.

* Run the `config_gen_data.sh` script from the `gemmini-data-collection` directory, as our paths assume that this is your current working directory.
- Note that all the tests you specificed in Step 2 will run **in parallel** on separate processes. Make sure you are running on a server with sufficient RAM for all those tests!

## Detailed Script Descriptions
### `gemmini_data_collection.py`

This script generates three other critical scripts that facilitate the data collection process, described below.

Each call to the `main` function of this script generates an instance of a template test bench (ex. GEMM, 2D-Conv) for Gemmini simulation; it also mutates the below scripts to account for the instance:

* `gemmini/data-collection-vcs.sh`

* Uses the `gemmini/scripts/run-vcs.sh` script to run a faster cycle-accurate Gemmini simulation for each instance generated by the `gemmini_data_collection.py` script

* Output containing the cycle count directed to `gemmini/data-collection-output`.

* `gemmini/data-collection-midas.sh midas_dram_model`

* Uses the `gemmini/scripts/run-midas.sh` script to run a cycle-accurate Gemmini simulation with a more precise DRAM model for each instance generated by the `gemmini_data_collection.py` script

* Output containing the cycle count is directed to `gemmini/data-collection-output`.

* `gemmini/data-collection-spike.sh`

* Uses the `gemmini/scripts/run-spike.sh` script to run through the "functionality" of each instance generated by the `gemmini_data_collection.py` script

* When Spike is called, a switch will enable the tiling factors for the job to be outputted instead of the cycle count (does not happen when running `gemmini/data-collection-vcs.sh` or `gemmini/data-collection-midas.sh`)

* Output containing tiling factors directed to `gemmini/data-collection-output`

* `clean.sh`

* Cleans the output of `gemmini/software/gemmini-rocc-tests/build.sh` script

* Deletes `gemmini/data-collection-output-configs` folder

* Deletes `gemmini/data-collection-output` folder

* Deletes `gemmini/data-collection-vcs.sh` script

* Deletes `gemmini/data-collection-midas.sh` script

* Deletes `gemmini/data-collection-spike.sh` script

* Deletes all test bench instance `C` files generated from the `gemmini_data_collection.py` script

* Resets `Makefile` to remove instruction to build test bench instances

### `gen_data.sh tile|cycle vcs|verilator|midas [midas_dram_model]`

* If called with the `tile` parameter, script generates output with tiling factors; if called with the `cycle` parameter, script generates output with simulation cycles

* Cleans all prior output via `clean.sh`

* Runs `gemmini_data_collection.py`

* If called with the `tile` parameter, turns switch to print tiling factors on; if called with the `cycle` parameter, turns switch to print tiling factors off (affects `gemmini/software/gemmini-rocc-tests/include/gemmini.h`), and instead prints the cycle count.

* Builds the `bareMetalC` tests

* If called with the `tile` parameter, calls `gemmini/data-collection-spike.sh`

* If called with the `cycle` parameter, then a second input parameter is expected (`vcs`, `verilator`, or `midas` to specify which simulation)

* If called with the `vcs` parameter, calls `gemmini/data-collection-vcs.sh`

* If called with the `midas` parameter, calls `gemmini/data-collection-midas.sh`

* **Note:** The `midas_dram_model` parameter is only required if the script is called with the `midas` argument

### `config_gen_data.sh config_name vcs|verilator|midas [midas_dram_model]`

* Sets the active Gemmini configuration in `gemmini/src/main/scala/gemmini/CustomConfigs.scala` to the specified `config_name` Scala variable name provided as a parameter

* If you want to use the default configuration, you should pass in `baselineInferenceConfig` as the first parameter

* Builds Spike and _either_ VCS, _or_ Verilator, _or_ Midas based on the chosen configuration using `gemmini/scripts/build-spike.sh` and _either_ `gemmini/scripts/build-vcs.sh`, _or_ `gemmini/scripts/build-verilator.sh`, _or_ `gemmini/scripts/build-midas.sh` respectively.

* Runs `gen_data.sh tile` and `gen_data.sh cycle [vcs|verilator|midas]` to collect both tiling factor data and simulation cycle count data.

* Places output in subfolders within the `data-collection-output-configs` folder; the subfolders follow the naming format `data-collection-output-<spike/vcs/verilator/midas>-<config_name>` where the `<>` indicates variability of naming

* **Note:** The `midas_dram_model` parameter is only required if the script is called with the `midas` argument

31 changes: 31 additions & 0 deletions gemmini-data-collection/config_gen_data.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash

cd ../../..
sed -i "/val customConfig =/c\ val customConfig = $1" src/main/scala/gemmini/CustomConfigs.scala
sed -i "/val customConfig =/c\ val customConfig = $1" configs/GemminiCustomConfigs.scala
if [ "$2" = "vcs" ]; then
./scripts/build-vcs.sh
elif [ "$2" = "verilator" ]; then
./scripts/build-verilator.sh
elif [ "$2" = "midas" ]; then
./scripts/build-midas.sh $3
else
echo "Invalid second paramter passed into gen-data.sh: should be 'vcs', 'verilator' or 'midas'"
exit 1
fi

./scripts/build-spike.sh
cd software/gemmini-rocc-tests/gemmini-data-collection

result_dir=../../../data-collection-output
tiling_dir=../../../data-collection-output-configs/data-collection-output-tiling-factors-$1
cycle_dir=../../../data-collection-output-configs/data-collection-output-cycles-$2-$1

mkdir -p $tiling_dir
mkdir -p $cycle_dir

bash gen_data.sh tile
mv $result_dir/* $tiling_dir/ && rmdir $result_dir
bash gen_data.sh cycle $2 $3
mv $result_dir/* $cycle_dir/ && rmdir $result_dir

92 changes: 92 additions & 0 deletions gemmini-data-collection/gemmini_data_collection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import sys
import tests

def main(keywords, replacement, template_file, new_file):
"""
Search TEMPLATE_FILE for KEYWORDS and replace respective keywords with REPLACEMENT. Write changes to NEW_FILE.
Update Makefile with new filename for target.
"""
assert (len(keywords) == len(replacement)), "Number of keywords needs to be the same as number of replacement words"

with open('templates/'+template_file+'.c', 'r') as file:
filedata = file.read()

for i in range(len(keywords)):
filedata = filedata.replace('%'+keywords[i]+'%', replacement[i])

with open('../bareMetalC/'+new_file+'.c', 'w') as file:
file.write(filedata)

print("Created " + new_file + " from " + template_file)

with open('../bareMetalC/Makefile', 'r') as file:
filedata = file.read()

filedata = filedata.replace("tests = \\", "tests = \\\n\t"+new_file+"\\")

with open('../bareMetalC/Makefile', 'w') as file:
filedata = file.write(filedata)

print("Updated Makefile")

with open('../../../data-collection-vcs.sh', 'r') as file:
filedata = file.read()

filedata = filedata + "./scripts/run-vcs.sh " + new_file + " > data-collection-output/" + new_file + "-vcs.txt &\n"

with open('../../../data-collection-vcs.sh', 'w') as file:
filedata = file.write(filedata)

print("Updated data-collection-vcs.sh script")

with open('../../../data-collection-verilator.sh', 'r') as file:
filedata = file.read()

filedata = filedata + "./scripts/run-verilator.sh " + new_file + " > data-collection-output/" + new_file + "-verilator.txt &\n"

with open('../../../data-collection-verilator.sh', 'w') as file:
filedata = file.write(filedata)

print("Updated data-collection-verilator.sh script")

with open('../../../data-collection-midas.sh', 'r') as file:
filedata = file.read()

filedata = filedata + "./scripts/run-midas.sh $1 " + new_file + " > data-collection-output/" + new_file + "-midas.txt &\n"

with open('../../../data-collection-midas.sh', 'w') as file:
filedata = file.write(filedata)

print("Updated data-collection-midas.sh script")

with open('../../../data-collection-spike.sh', 'r') as file:
filedata = file.read()

filedata = filedata + "./scripts/run-spike.sh " + new_file + " > data-collection-output/" + new_file + "-spike.txt &\n"

with open('../../../data-collection-spike.sh', 'w') as file:
filedata = file.write(filedata)

print("Updated data-collection-spike.sh script")

with open('clean.sh', 'a') as file:
file.write('rm ../bareMetalC/' + new_file + '.c\n')

print("Updated clean.sh script")


if __name__ == "__main__":
for fname in 'vcs', 'verilator', 'midas', 'spike':
with open('../../../data-collection-' + fname + '.sh', 'w') as file:
file.write("#!/bin/bash\n\nmkdir -p data-collection-output\n")

with open('clean.sh', 'w') as file:
file.write('#!/bin/bash\n\nrm -rf ../../../data-collection-output\nrm ../../../data-collection-vcs.sh\nrm ../../../data-collection-verilator.sh\nrm ../../../data-collection-midas.sh\nrm ../../../data-collection-spike.sh\ncp og_baremetal_Makefile ../bareMetalC/Makefile\ncd ..\n./build.sh clean\ncd gemmini-data-collection\n')

for test in tests.tests:
main(*test)

for fname in 'vcs', 'verilator', 'midas', 'spike':
with open('../../../data-collection-' + fname + '.sh', 'a') as file:
file.write("wait\n")

Loading