mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-11 06:05:49 +00:00
revived checkpointing and hacked it up to generate a trace starting at the checkpoint
This commit is contained in:
parent
c1a50b38c3
commit
e9e358cdd0
4
.gitignore
vendored
4
.gitignore
vendored
@ -54,6 +54,10 @@ examples/C/sum/sum
|
||||
examples/C/fir/fir
|
||||
linux/devicetree/debug/*
|
||||
!linux/devicetree/debug/dump-dts.sh
|
||||
linux/testvector-generation/genCheckpoint.gdb
|
||||
linux/testvector-generation/silencePipe
|
||||
linux/testvector-generation/silencePipe.control
|
||||
linux/testvector-generation/fixBinMem
|
||||
*.dtb
|
||||
synthDC/WORK
|
||||
synthDC/alib-52
|
||||
|
17
linux/testvector-generation/Makefile
Normal file
17
linux/testvector-generation/Makefile
Normal file
@ -0,0 +1,17 @@
|
||||
SHELL = /bin/sh
|
||||
|
||||
CFLAG = -Wall -g
|
||||
CC = clang
|
||||
|
||||
all: fixBinMem silencePipe
|
||||
|
||||
fixBinMem: fixBinMem.c
|
||||
${CC} ${CFLAGS} fixBinMem.c -o fixBinMem
|
||||
chmod +x fixBinMem
|
||||
|
||||
silencePipe: silencePipe.c
|
||||
${CC} ${CFLAGS} silencePipe.c -o silencePipe
|
||||
chmod +x silencePipe
|
||||
clean:
|
||||
-rm -f fixBinMem
|
||||
-rm -f silencePipe
|
71
linux/testvector-generation/createGenCheckpointScript.py
Executable file
71
linux/testvector-generation/createGenCheckpointScript.py
Executable file
@ -0,0 +1,71 @@
|
||||
#! /usr/bin/python3
|
||||
import sys
|
||||
|
||||
if len(sys.argv) != 8:
|
||||
sys.exit("""Error createGenCheckpointScript.py expects 7 args:
|
||||
<TCP port number>
|
||||
<path to vmlinux>
|
||||
<checkpoint instruction count>
|
||||
<path to GDB checkpoint state dump>
|
||||
<path to GDB ram dump>
|
||||
<checkpoint pc address>
|
||||
<number of times pc has already been hit before checkpoint>""")
|
||||
|
||||
tcpPort=sys.argv[1]
|
||||
vmlinux=sys.argv[2]
|
||||
instrCount=sys.argv[3]
|
||||
statePath=sys.argv[4]
|
||||
ramPath=sys.argv[5]
|
||||
checkPC=sys.argv[6]
|
||||
checkPCoccurences=sys.argv[7]
|
||||
|
||||
GDBscript = f"""
|
||||
# GDB config
|
||||
set pagination off
|
||||
set logging overwrite on
|
||||
set logging redirect on
|
||||
set confirm off
|
||||
|
||||
# Connect to QEMU session
|
||||
target extended-remote :{tcpPort}
|
||||
|
||||
# QEMU Config
|
||||
maintenance packet Qqemu.PhyMemMode:1
|
||||
|
||||
# Symbol file
|
||||
file {vmlinux}
|
||||
|
||||
# Silence Trace Generation
|
||||
shell echo 1 > ./silencePipe.control
|
||||
|
||||
# Step over reset vector into actual code
|
||||
stepi 100
|
||||
# Proceed to checkpoint
|
||||
print "GDB proceeding to checkpoint at {instrCount} instrs, pc {checkPC}\\n"
|
||||
b *0x{checkPC}
|
||||
ignore 1 {checkPCoccurences}
|
||||
c
|
||||
print "Reached checkpoint at {instrCount} instrs\\n"
|
||||
|
||||
# Log all registers to a file
|
||||
printf "GDB storing state to {statePath}\\n"
|
||||
set logging file {statePath}
|
||||
set logging on
|
||||
info all-registers
|
||||
set logging off
|
||||
|
||||
# Log main memory to a file
|
||||
print "GDB storing RAM to {ramPath}\\n"
|
||||
#dump binary memory {ramPath} 0x80000000 0xffffffff
|
||||
#dump binary memory {ramPath} 0x80000000 0x80ffffff
|
||||
|
||||
# Generate Trace Until End
|
||||
shell echo 0 > ./silencePipe.control
|
||||
# Do this by setting an impossible breakpoint
|
||||
b *0x1000
|
||||
del 1
|
||||
c
|
||||
"""
|
||||
GDBscriptFile = open("genCheckpoint.gdb",'w')
|
||||
GDBscriptFile.write(GDBscript)
|
||||
GDBscriptFile.close()
|
33
linux/testvector-generation/fixBinMem.c
Normal file
33
linux/testvector-generation/fixBinMem.c
Normal file
@ -0,0 +1,33 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 3){
|
||||
fprintf(stderr, "Expected 2 arguments: <raw GDB dump> <output binary>\n");
|
||||
exit(1);
|
||||
}
|
||||
char* rawGDBfilePath = argv[1];
|
||||
FILE* rawGDBfile;
|
||||
if ((rawGDBfile = fopen(rawGDBfilePath,"rb"))==NULL) {
|
||||
fprintf(stderr, "File not found: %s\n",rawGDBfilePath);
|
||||
exit(1);
|
||||
}
|
||||
char* outFilePath = argv[2];
|
||||
FILE* outFile = fopen(outFilePath,"w");
|
||||
uint64_t qemuWord;
|
||||
uint64_t verilogWord;
|
||||
int bytesReturned=0;
|
||||
do {
|
||||
bytesReturned=fread(&qemuWord, 8, 1, rawGDBfile);
|
||||
verilogWord = (((qemuWord>>0 )&0xff)<<56 |
|
||||
((qemuWord>>8 )&0xff)<<48 |
|
||||
((qemuWord>>16)&0xff)<<40 |
|
||||
((qemuWord>>24)&0xff)<<32 |
|
||||
((qemuWord>>32)&0xff)<<24 |
|
||||
((qemuWord>>40)&0xff)<<16 |
|
||||
((qemuWord>>48)&0xff)<<8 |
|
||||
((qemuWord>>56)&0xff)<<0);
|
||||
fwrite(&verilogWord, 8, 1, outFile);
|
||||
} while(bytesReturned!=0);
|
||||
return 0;
|
||||
}
|
89
linux/testvector-generation/genCheckpoint.sh
Executable file
89
linux/testvector-generation/genCheckpoint.sh
Executable file
@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
tcpPort=1237
|
||||
imageDir=$RISCV/buildroot/output/images
|
||||
tvDir=$RISCV/linux-testvectors
|
||||
recordFile="$tvDir/all.qemu"
|
||||
traceFile="$tvDir/all_up_to_498.txt"
|
||||
|
||||
# Parse Commandline Arg
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "genCheckpoint requires 1 argument: <num instrs>" >&2
|
||||
exit 1
|
||||
fi
|
||||
instrs=$1
|
||||
if ! [ "$instrs" -eq "$instrs" ] 2> /dev/null
|
||||
then
|
||||
echo "Error expected integer number of instructions, got $instrs" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
checkPtDir="$tvDir/checkpoint$instrs"
|
||||
outTraceFile="$checkPtDir/all.txt"
|
||||
interruptsFile="$checkPtDir/interrupts.txt"
|
||||
rawStateFile="$checkPtDir/stateGDB.txt"
|
||||
rawRamFile="$checkPtDir/ramGDB.bin"
|
||||
ramFile="$checkPtDir/ram.bin"
|
||||
|
||||
read -p "This scripts is going to create a checkpoint at $instrs instrs.
|
||||
Is that what you wanted? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
echo "Creating checkpoint at $instrs instructions!"
|
||||
make silencePipe
|
||||
|
||||
# Create Output Directory
|
||||
echo "Elevating permissions to create $checkPtDir and stuff inside it"
|
||||
sudo mkdir -p $checkPtDir
|
||||
sudo chown -R cad:users $checkPtDir
|
||||
sudo chmod -R a+rw $checkPtDir
|
||||
sudo touch $outTraceFile
|
||||
sudo chmod a+rw $outTraceFile
|
||||
sudo touch $interruptsFile
|
||||
sudo chmod a+rw $interruptsFile
|
||||
sudo touch $rawStateFile
|
||||
sudo chmod a+rw $rawStateFile
|
||||
sudo touch $rawRamFile
|
||||
sudo chmod a+rw $rawRamFile
|
||||
sudo touch $ramFile
|
||||
sudo chmod a+rw $ramFile
|
||||
|
||||
# Identify instruction in trace
|
||||
instr=$(sed "${instrs}q;d" "$traceFile")
|
||||
echo "Found ${instrs}th instr: ${instr}"
|
||||
pc=$(echo $instr | cut -d " " -f1)
|
||||
asm=$(echo $instr | cut -d " " -f2)
|
||||
occurences=$(($(head -$instrs "$traceFile" | grep -c "${pc} ${asm}")-1))
|
||||
echo "It occurs ${occurences} times before the ${instrs}th instr."
|
||||
|
||||
# Create GDB script because GDB is terrible at handling arguments / variables
|
||||
./createGenCheckpointScript.py $tcpPort $imageDir/vmlinux $instrs $rawStateFile $rawRamFile $pc $occurences
|
||||
|
||||
# GDB+QEMU
|
||||
echo "Starting QEMU in replay mode with attached GDB script at $(date +%H:%M:%S)"
|
||||
(qemu-system-riscv64 \
|
||||
-M virt -dtb $imageDir/wally-virt.dtb \
|
||||
-nographic \
|
||||
-bios $imageDir/fw_jump.elf -kernel $imageDir/Image -append "root=/dev/vda ro" -initrd $imageDir/rootfs.cpio \
|
||||
-singlestep -rtc clock=vm -icount shift=0,align=off,sleep=on,rr=replay,rrfile=$recordFile \
|
||||
-d nochain,cpu,in_asm,int \
|
||||
-gdb tcp::$tcpPort -S \
|
||||
2>&1 >/dev/null | ./silencePipe | ./parseQEMUtoGDB/parseQEMUtoGDB_run.py | ./parseGDBtoTrace/parseGDBtoTrace_run.py $interruptsFile | ./remove_dup.awk > $outTraceFile) \
|
||||
& riscv64-unknown-elf-gdb --quiet -ex "source genCheckpoint.gdb"
|
||||
echo "Completed GDB script at $(date +%H:%M:%S)"
|
||||
|
||||
# Post-Process GDB outputs
|
||||
./parseState.py "$checkPtDir"
|
||||
echo "Changing Endianness at $(date +%H:%M:%S)"
|
||||
make fixBinMem
|
||||
./fixBinMem "$rawRamFile" "$ramFile"
|
||||
#echo "Creating truncated trace at $(date +%H:%M:%S)"
|
||||
#tail -n+$instrs "$tvDir/$traceFile" > "$checkPtDir/$traceFile"
|
||||
echo "Checkpoint completed at $(date +%H:%M:%S)"
|
||||
|
||||
# Cleanup
|
||||
echo "Elevating permissions to restrict write access to $checkPtDir"
|
||||
sudo chown -R cad:users $checkPtDir
|
||||
sudo chmod -R go-w $checkPtDir
|
||||
fi
|
||||
|
99
linux/testvector-generation/parseState.py
Executable file
99
linux/testvector-generation/parseState.py
Executable file
@ -0,0 +1,99 @@
|
||||
#! /usr/bin/python3
|
||||
import sys, os
|
||||
|
||||
################
|
||||
# Helper Funcs #
|
||||
################
|
||||
|
||||
def tokenize(string):
|
||||
tokens = []
|
||||
token = ''
|
||||
whitespace = 0
|
||||
prevWhitespace = 0
|
||||
for char in string:
|
||||
prevWhitespace = whitespace
|
||||
whitespace = char in ' \t\n'
|
||||
if (whitespace):
|
||||
if ((not prevWhitespace) and (token != '')):
|
||||
tokens.append(token)
|
||||
token = ''
|
||||
else:
|
||||
token = token + char
|
||||
return tokens
|
||||
|
||||
#############
|
||||
# Main Code #
|
||||
#############
|
||||
print("Begin parsing state.")
|
||||
|
||||
# Parse Args
|
||||
if len(sys.argv) != 2:
|
||||
sys.exit('Error parseState.py expects 1 arg:\n parseState.py <path_to_checkpoint_dir>')
|
||||
outDir = sys.argv[1]+'/'
|
||||
stateGDBpath = outDir+'stateGDB.txt'
|
||||
if not os.path.exists(stateGDBpath):
|
||||
sys.exit('Error input file '+stateGDBpath+'not found')
|
||||
|
||||
singleCSRs = ['pc','mip','mie','mscratch','mcause','mepc','mtvec','medeleg','mideleg','sscratch','scause','sepc','stvec','sedeleg','sideleg','satp','mstatus','priv']
|
||||
# priv (current privilege mode) isn't technically a CSR but we can log it with the same machinery
|
||||
thirtyTwoBitCSRs = ['mcounteren','scounteren']
|
||||
listCSRs = ['hpmcounter','pmpaddr']
|
||||
pmpcfg = ['pmpcfg']
|
||||
|
||||
# Initialize List CSR files to empty
|
||||
# (because later we'll open them in append mode)
|
||||
for csr in listCSRs+pmpcfg:
|
||||
outFileName = 'checkpoint-'+csr.upper()
|
||||
outFile = open(outDir+outFileName, 'w')
|
||||
outFile.close()
|
||||
|
||||
# Initial State for Main Loop
|
||||
currState = 'regFile'
|
||||
regFileIndex = 0
|
||||
outFileName = 'checkpoint-RF'
|
||||
outFile = open(outDir+outFileName, 'w')
|
||||
|
||||
# Main Loop
|
||||
with open(stateGDBpath, 'r') as stateGDB:
|
||||
for line in stateGDB:
|
||||
line = tokenize(line)
|
||||
name = line[0]
|
||||
val = line[1][2:]
|
||||
if (currState == 'regFile'):
|
||||
if (regFileIndex == 0 and name != 'zero'):
|
||||
print('Whoops! Expected regFile registers to come first, starting with zero')
|
||||
exit(1)
|
||||
if (name != 'zero'):
|
||||
# Wally doesn't need to know zero=0
|
||||
outFile.write(val+'\n')
|
||||
regFileIndex += 1
|
||||
if (regFileIndex == 32):
|
||||
outFile.close()
|
||||
currState = 'CSRs'
|
||||
elif (currState == 'CSRs'):
|
||||
if name in singleCSRs:
|
||||
outFileName = 'checkpoint-'+name.upper()
|
||||
outFile = open(outDir+outFileName, 'w')
|
||||
outFile.write(val+'\n')
|
||||
outFile.close()
|
||||
elif name in thirtyTwoBitCSRs:
|
||||
outFileName = 'checkpoint-'+name.upper()
|
||||
outFile = open(outDir+outFileName, 'w')
|
||||
val = int(val,16) & 0xffffffff
|
||||
outFile.write(hex(val)[2:]+'\n')
|
||||
outFile.close()
|
||||
elif name.strip('0123456789') in listCSRs:
|
||||
outFileName = 'checkpoint-'+name.upper().strip('0123456789')
|
||||
outFile = open(outDir+outFileName, 'a')
|
||||
outFile.write(val+'\n')
|
||||
outFile.close()
|
||||
elif name.strip('0123456789') in pmpcfg:
|
||||
outFileName = 'checkpoint-'+name.upper().strip('0123456789')
|
||||
outFile = open(outDir+outFileName, 'a')
|
||||
fourPmp = int(val,16)
|
||||
for i in range(0,4):
|
||||
byte = (fourPmp >> 8*i) & 0xff
|
||||
outFile.write(hex(byte)[2:]+'\n')
|
||||
outFile.close()
|
||||
|
||||
print("Finished parsing state!")
|
19
linux/testvector-generation/silencePipe.c
Normal file
19
linux/testvector-generation/silencePipe.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
while (1) {
|
||||
FILE* controlFile = fopen("silencePipe.control", "r");
|
||||
char silenceChar = getc(controlFile);
|
||||
fclose(controlFile);
|
||||
ssize_t lineSize = getline(&line, &len, stdin);
|
||||
if (silenceChar!='1') {
|
||||
printf("%s",line);
|
||||
}
|
||||
}
|
||||
free(line);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user