mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-02 09:45:18 +00:00
Merge branch 'main' of https://github.com/openhwgroup/cvw into Z_enable
This commit is contained in:
commit
4dff7d17e6
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -25,9 +25,10 @@
|
||||
sparseCheckout = true
|
||||
path = addins/verilog-ethernet
|
||||
url = https://github.com/rosethompson/verilog-ethernet.git
|
||||
[submodule "cvw-arch-verif"]
|
||||
[submodule "addins/cvw-arch-verif"]
|
||||
path = addins/cvw-arch-verif
|
||||
url = https://github.com/openhwgroup/cvw-arch-verif
|
||||
ignore = dirty
|
||||
[submodule "addins/riscvISACOV"]
|
||||
path = addins/riscvISACOV
|
||||
url = https://github.com/riscv-verification/riscvISACOV.git
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit bbcba78647080dee82e96bc1b8ff9cd9a3cf7fa1
|
||||
Subproject commit 6d658b7b42c83fd584008d72964cc75d0876b769
|
@ -1 +1 @@
|
||||
Subproject commit ce04b4930545ae4c81e2f3b6f6935e2aac08679e
|
||||
Subproject commit 3843c736e427a2b52a0d06e6220b073afa4be401
|
@ -88,6 +88,18 @@ foreach my $key (@derivnames) {
|
||||
open(my $unmod, $configunmod) or die "Could not open file '$configunmod' $!";
|
||||
open(my $fh, '>>', $config) or die "Could not open file '$config' $!";
|
||||
|
||||
# Create symlink to imperas.ic for deriv buildroot
|
||||
if ($key eq "buildroot") {
|
||||
my $baseimperas_ic = "$ENV{WALLY}/config/$basederiv{$key}/imperas.ic";
|
||||
if (! -e $baseimperas_ic) {
|
||||
my $baseimperas_ic = "$ENV{WALLY}/config/deriv/$basederiv{$key}/config.vh";
|
||||
}
|
||||
if (-e $baseimperas_ic) { # If imperas.ic exists for base derivative, create hardlink to it
|
||||
my $imperas_ic = "$dir/imperas.ic";
|
||||
system("ln -T $baseimperas_ic $imperas_ic");
|
||||
}
|
||||
}
|
||||
|
||||
my $datestring = localtime();
|
||||
my %hit = ();
|
||||
print $fh "// Config $key automatically derived from $basederiv{$key} on $datestring using derivgen.pl\n";
|
||||
|
@ -261,9 +261,6 @@ class TestRunner:
|
||||
cvw = folder.joinpath("cvw")
|
||||
self.logger.info(f"cvw is: {cvw}")
|
||||
|
||||
# set the WALLY environmental variable to the new repository
|
||||
os.environ["WALLY"] = str(cvw)
|
||||
|
||||
self.cvw = cvw
|
||||
self.sim_dir = cvw.joinpath("bin")
|
||||
self.base_parent_dir = folder
|
||||
@ -292,11 +289,11 @@ class TestRunner:
|
||||
output_file = self.log_dir.joinpath(f"make-{target}-output.log")
|
||||
else: output_file = self.log_dir.joinpath(f"make-output.log")
|
||||
|
||||
# Execute make with target and cores/2
|
||||
# Source setup script and execute make with target and cores/2
|
||||
if target:
|
||||
command = ["make", target, "--jobs=$(($(nproc)/2))"]
|
||||
command = [f"source {os.path.join(self.cvw, 'setup.sh')} > /dev/null && make {target} --jobs=$(($(nproc)/2))"]
|
||||
else:
|
||||
command = ["make", "--jobs=$(($(nproc)/2))"]
|
||||
command = [f"source {os.path.join(self.cvw, 'setup.sh')} > /dev/null && make --jobs=$(($(nproc)/2))"]
|
||||
|
||||
self.logger.info(f"Command used in directory {makefile_location}: {' '.join(command)}")
|
||||
|
||||
@ -305,7 +302,7 @@ class TestRunner:
|
||||
formatted_datetime = self.current_datetime.strftime("%Y-%m-%d %H:%M:%S")
|
||||
f.write(formatted_datetime)
|
||||
f.write("\n\n")
|
||||
result = subprocess.run(command, stdout=f, stderr=subprocess.STDOUT, text=True, shell=True)
|
||||
result = subprocess.run(command, stdout=f, stderr=subprocess.STDOUT, text=True, shell=True, executable="/bin/bash")
|
||||
|
||||
# Execute the command using a subprocess and not save the output
|
||||
#result = subprocess.run(command, text=True)
|
||||
@ -334,12 +331,16 @@ class TestRunner:
|
||||
output_file = self.log_dir.joinpath(f"{test_name}-output.log")
|
||||
os.chdir(self.sim_dir)
|
||||
|
||||
# Source setup script and delete output from log on whatever test command gets run
|
||||
command = f"source {os.path.join(self.cvw, 'setup.sh')} > /dev/null && "
|
||||
|
||||
if test_extensions:
|
||||
command = [test_type, test_name] + test_extensions
|
||||
commandext = [test_type, test_name] + test_extensions
|
||||
self.logger.info(f"Command used to run tests in directory {self.sim_dir}: {test_type} {test_name} {' '.join(test_extensions)}")
|
||||
else:
|
||||
command = [test_type, test_name]
|
||||
commandext = [test_type, test_name]
|
||||
self.logger.info(f"Command used to run tests in directory {self.sim_dir}: {test_type} {test_name}")
|
||||
command += " ".join(commandext)
|
||||
|
||||
|
||||
# Execute the command using subprocess and save the output into a file
|
||||
@ -348,15 +349,15 @@ class TestRunner:
|
||||
formatted_datetime = self.current_datetime.strftime("%Y-%m-%d %H:%M:%S")
|
||||
f.write(formatted_datetime)
|
||||
f.write("\n\n")
|
||||
result = subprocess.run(command, stdout=f, stderr=subprocess.STDOUT, text=True)
|
||||
result = subprocess.run(command, stdout=f, stderr=subprocess.STDOUT, text=True, shell=True, executable="/bin/bash")
|
||||
except Exception as e:
|
||||
self.logger.error("There was an error in running the tests in the run_tests function: {e}")
|
||||
self.logger.error(f"There was an error in running the tests in the run_tests function: {e}")
|
||||
# Check if the command executed successfuly
|
||||
if result.returncode or result.returncode == 0:
|
||||
self.logger.info(f"Test ran successfuly. Test type: {test_type}, test name: {test_name}, test extension: {' '.join(test_extensions)}")
|
||||
self.logger.info(f"Test ran successfuly. Test name: {test_name}, test extension: {' '.join(test_extensions)}")
|
||||
return True, output_file
|
||||
else:
|
||||
self.logger.error(f"Error making test. Test type: {test_type}, test name: {test_name}, test extension: {' '.join(test_extensions)}")
|
||||
self.logger.error(f"Error making test. Test name: {test_name}, test extension: {' '.join(test_extensions)}")
|
||||
return False, output_file
|
||||
|
||||
|
||||
|
@ -539,7 +539,7 @@ def main():
|
||||
num_fail = 0
|
||||
results = {}
|
||||
for config in configs:
|
||||
results[config] = pool.apply_async(run_test_case,(config,))
|
||||
results[config] = pool.apply_async(run_test_case,(config, args.dryrun))
|
||||
for (config,result) in results.items():
|
||||
try:
|
||||
num_fail+=result.get(timeout=TIMEOUT_DUR)
|
||||
|
6
bin/wsim
6
bin/wsim
@ -65,7 +65,7 @@ if(args.testsuite.endswith('.elf') and args.elf == ""): # No --elf argument; che
|
||||
print("ELF file not found: " + args.testsuite)
|
||||
exit(1)
|
||||
|
||||
if(args.lockstep and not args.testsuite.endswith('.elf')):
|
||||
if(args.lockstep and not args.testsuite.endswith('.elf') and not args.testsuite == "buildroot"):
|
||||
print(f"Invalid Options. Cannot run a testsuite, {args.testsuite} with lockstep. Must run a single elf.")
|
||||
exit(1)
|
||||
|
||||
@ -90,7 +90,9 @@ else: EnableLog = 0
|
||||
prefix = ""
|
||||
if (args.lockstep or args.lockstepverbose or args.fcov or args.fcovimp):
|
||||
if (args.sim == "questa" or args.sim == "vcs"):
|
||||
prefix = "IMPERAS_TOOLS=" + WALLY + "/config/"+args.config+"/imperas.ic"
|
||||
prefix = "IMPERAS_TOOLS=" + os.path.join(WALLY, "config", args.config, "imperas.ic")
|
||||
if not os.path.isfile(prefix): # If config is a derivative, look for imperas.ic in derivative configs
|
||||
prefix = "IMPERAS_TOOLS=" + os.path.join(WALLY, "config", "deriv", args.config, "imperas.ic")
|
||||
# Force Questa to use 64-bit mode, sometimes it defaults to 32-bit even on 64-bit machines
|
||||
if (args.sim == "questa"):
|
||||
prefix = "MTI_VCO_MODE=64 " + prefix
|
||||
|
@ -37,3 +37,5 @@
|
||||
|
||||
// Privileged extensions
|
||||
`include "ZicsrM_coverage.svh"
|
||||
`include "RV32VM_coverage.svh"
|
||||
`include "RV32VM_PMP_coverage.svh"
|
||||
|
@ -5,3 +5,22 @@ wally/wallypipelinedcore.sv: logic InstrM
|
||||
lsu/lsu.sv: logic IEUAdrM
|
||||
lsu/lsu.sv: logic MemRWM
|
||||
mmu/hptw.sv: logic SATP_REGW
|
||||
uncore/spi_apb.sv: logic ShiftIn
|
||||
uncore/spi_apb.sv: logic ReceiveShiftReg
|
||||
uncore/spi_apb.sv: logic SCLKenable
|
||||
uncore/spi_apb.sv: logic SampleEdge
|
||||
uncore/spi_apb.sv: logic Active
|
||||
uncore/spi_apb.sv: statetype state
|
||||
uncore/spi_apb.sv: typedef rsrstatetype
|
||||
uncore/spi_apb.sv: logic SPICLK
|
||||
uncore/spi_apb.sv: logic SPIOut
|
||||
uncore/spi_apb.sv: logic SPICS
|
||||
uncore/spi_apb.sv: logic SckMode
|
||||
uncore/spi_apb.sv: logic SckDiv
|
||||
uncore/spi_apb.sv: logic ShiftEdge
|
||||
uncore/spi_apb.sv: logic TransmitShiftRegLoad
|
||||
uncore/spi_apb.sv: logic TransmitShiftReg
|
||||
uncore/spi_apb.sv: logic TransmitData
|
||||
uncore/spi_apb.sv: logic ReceiveData
|
||||
uncore/spi_apb.sv: logic ReceiveShiftRegEndian
|
||||
uncore/spi_apb.sv: logic ASR
|
||||
|
@ -191,7 +191,6 @@ set_property port_width 1 [get_debug_ports u_ila_0/probe33]
|
||||
set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe33]
|
||||
connect_debug_port u_ila_0/probe33 [get_nets [list {wallypipelinedsoc/uncoregen.uncore/sdc.sdc/TransmitFIFOReadEmpty} ]]
|
||||
|
||||
|
||||
# the debug hub has issues with the clocks from the mmcm so lets give up an connect to the 100Mhz input clock.
|
||||
#connect_debug_port dbg_hub/clk [get_nets default_100mhz_clk]
|
||||
connect_debug_port dbg_hub/clk [get_nets CPUCLK]
|
||||
|
@ -69,8 +69,8 @@ PreProcessFiles:
|
||||
./insert_debug_comment.sh
|
||||
# This line allows the Bootloader to be loaded in a Block RAM on the FPGA
|
||||
sed -i "s/bit \[DATA_WIDTH-1:0\].*ROM.*/(\* rom_style=\"block\" \*) &/g" ../src/CopiedFiles_do_not_add_to_repo/generic/mem/rom1p1r.sv
|
||||
sed -i 's/$$WALLY/\.\.\/\.\.\/\.\.\//g' ../src/CopiedFiles_do_not_add_to_repo/generic/mem/rom1p1r.sv
|
||||
sed -i 's/$$WALLY/\.\.\/\.\.\/\.\.\//g' ../src/CopiedFiles_do_not_add_to_repo/generic/mem/ram1p1rwbe.sv
|
||||
sed -i 's/$$WALLY/\.\.\/\.\.\//g' ../src/CopiedFiles_do_not_add_to_repo/generic/mem/rom1p1r.sv
|
||||
sed -i 's/$$WALLY/\.\.\/\.\.\//g' ../src/CopiedFiles_do_not_add_to_repo/generic/mem/ram1p1rwbe.sv
|
||||
|
||||
# build the Zero stage boot loader (ZSBL)
|
||||
.PHONY: zsbl
|
||||
|
@ -102,7 +102,8 @@ if {$board=="ArtyA7"} {
|
||||
} else {
|
||||
#source ../constraints/vcu-small-debug.xdc
|
||||
#source ../constraints/small-debug.xdc
|
||||
source ../constraints/small-debug-spi.xdc
|
||||
#source ../constraints/small-debug.xdc
|
||||
source ../constraints/big-debug-spi.xdc
|
||||
}
|
||||
|
||||
|
||||
|
@ -135,45 +135,45 @@ int disk_read(BYTE * buf, LBA_t sector, UINT count) {
|
||||
/* crc = crc16(crc, x); */
|
||||
/* } while (--n > 0); */
|
||||
|
||||
/* n = 512/8; */
|
||||
/* do { */
|
||||
/* // Send 8 dummy bytes (fifo should be empty) */
|
||||
/* for (j = 0; j < 8; j++) { */
|
||||
/* spi_sendbyte(0xff); */
|
||||
/* } */
|
||||
|
||||
/* // Reset counter. Process bytes AS THEY COME IN. */
|
||||
/* for (j = 0; j < 8; j++) { */
|
||||
/* while (!(read_reg(SPI_IP) & 2)) {} */
|
||||
/* uint8_t x = spi_readbyte(); */
|
||||
/* *p++ = x; */
|
||||
/* // crc = crc16(crc, x); */
|
||||
/* crc = ((crc << 8) ^ crctable[x ^ (crc >> 8)]) & 0xffff; */
|
||||
/* } */
|
||||
/* } while(--n > 0); */
|
||||
|
||||
n = 512;
|
||||
// Initially fill the transmit fifo
|
||||
for (j = 0; j < 8; j++) {
|
||||
spi_sendbyte(0xff);
|
||||
}
|
||||
|
||||
|
||||
while (n > 0) {
|
||||
// Wait for bytes to be received
|
||||
while (!(read_reg(SPI_IP) & 2)) {}
|
||||
// Read byte
|
||||
uint8_t x = spi_readbyte();
|
||||
// Send another dummy byte
|
||||
if (n > 8) {
|
||||
n = 512/8;
|
||||
do {
|
||||
// Send 8 dummy bytes (fifo should be empty)
|
||||
for (j = 0; j < 8; j++) {
|
||||
spi_sendbyte(0xff);
|
||||
}
|
||||
// Place received byte into memory
|
||||
*p++ = x;
|
||||
// Update CRC16 with fast table based method
|
||||
crc = ((crc << 8) ^ crctable[x ^ (crc >> 8)]) & 0xffff;
|
||||
n = n - 1;
|
||||
}
|
||||
|
||||
// Reset counter. Process bytes AS THEY COME IN.
|
||||
for (j = 0; j < 8; j++) {
|
||||
while (!(read_reg(SPI_IP) & 2)) {}
|
||||
uint8_t x = spi_readbyte();
|
||||
*p++ = x;
|
||||
// crc = crc16(crc, x);
|
||||
crc = ((crc << 8) ^ crctable[x ^ (crc >> 8)]) & 0xffff;
|
||||
}
|
||||
} while(--n > 0);
|
||||
|
||||
/* n = 512; */
|
||||
/* // Initially fill the transmit fifo */
|
||||
/* for (j = 0; j < 8; j++) { */
|
||||
/* spi_sendbyte(0xff); */
|
||||
/* } */
|
||||
|
||||
|
||||
/* while (n > 0) { */
|
||||
/* // Wait for bytes to be received */
|
||||
/* while (!(read_reg(SPI_IP) & 2)) {} */
|
||||
/* // Read byte */
|
||||
/* uint8_t x = spi_readbyte(); */
|
||||
/* // Send another dummy byte */
|
||||
/* if (n > 8) { */
|
||||
/* spi_sendbyte(0xff); */
|
||||
/* } */
|
||||
/* // Place received byte into memory */
|
||||
/* *p++ = x; */
|
||||
/* // Update CRC16 with fast table based method */
|
||||
/* crc = ((crc << 8) ^ crctable[x ^ (crc >> 8)]) & 0xffff; */
|
||||
/* n = n - 1; */
|
||||
/* } */
|
||||
|
||||
// Read CRC16 and check
|
||||
crc_exp = ((uint16_t)spi_dummy() << 8);
|
||||
|
@ -1,4 +1,30 @@
|
||||
#!/bin/bash
|
||||
###########################################
|
||||
## flash-sd.sh
|
||||
##
|
||||
## Written: Jacob Pease jacobpease@protonmail.com
|
||||
## Created: August 22, 2023
|
||||
##
|
||||
## Purpose: A script to flash an sd card with a bootable linux image.
|
||||
##
|
||||
## A component of the CORE-V-WALLY configurable RISC-V project.
|
||||
## https://github.com/openhwgroup/cvw
|
||||
##
|
||||
## Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||
##
|
||||
## SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||
##
|
||||
## Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||
## except in compliance with the License, or, at your option, the Apache License version 2.0. You
|
||||
## may obtain a copy of the License at
|
||||
##
|
||||
## https:##solderpad.org/licenses/SHL-2.1/
|
||||
##
|
||||
## Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||
## License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
## either express or implied. See the License for the specific language governing permissions
|
||||
## and limitations under the License.
|
||||
################################################################################################
|
||||
|
||||
# Exit on any error (return code != 0)
|
||||
# set -e
|
||||
|
80
linux/sdcard/write-bytes.sh
Executable file
80
linux/sdcard/write-bytes.sh
Executable file
@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
###########################################
|
||||
## write-bytes.sh
|
||||
##
|
||||
## Written: Jacob Pease jacobpease@protonmail.com
|
||||
## Created: November 2nd, 2024
|
||||
## Modified:
|
||||
##
|
||||
## Purpose: Write a sequence of bytes from text file to an output file and a flash card.
|
||||
##
|
||||
## A component of the CORE-V-WALLY configurable RISC-V project.
|
||||
## https://github.com/openhwgroup/cvw
|
||||
##
|
||||
## Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||
##
|
||||
## SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||
##
|
||||
## Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||
## except in compliance with the License, or, at your option, the Apache License version 2.0. You
|
||||
## may obtain a copy of the License at
|
||||
##
|
||||
## https:##solderpad.org/licenses/SHL-2.1/
|
||||
##
|
||||
## Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||
## License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
## either express or implied. See the License for the specific language governing permissions
|
||||
## and limitations under the License.
|
||||
################################################################################################
|
||||
|
||||
|
||||
# This file writes a bunch of bytes to the flash card based on a text
|
||||
# file input with bytes written in hexadecimal.
|
||||
|
||||
usage() { echo "Usage: $0 [-zh] [-b <path/to/buildroot>] <device>" 1>&2; exit 1; }
|
||||
|
||||
help() {
|
||||
echo "Usage: $0 [OPTIONS] <device>"
|
||||
echo " -i Input text file with hex bytes."
|
||||
echo " -b <path/to/buildroot> Output binary file."
|
||||
exit 0;
|
||||
}
|
||||
|
||||
INPUTFILE=""
|
||||
OUTPUTFILE=""
|
||||
|
||||
ARGS=()
|
||||
while [ $OPTIND -le "$#" ] ; do
|
||||
if getopts "hi:o:" arg ; then
|
||||
case "${arg}" in
|
||||
h) help
|
||||
;;
|
||||
i) INPUTFILE=${OPTARG}
|
||||
;;
|
||||
o) OUTPUTFILE=${OPTARG}
|
||||
;;
|
||||
esac
|
||||
else
|
||||
ARGS+=("${!OPTIND}")
|
||||
((OPTIND++))
|
||||
fi
|
||||
done
|
||||
|
||||
SDCARD=${ARGS[0]}
|
||||
|
||||
if [ ! -e $INPUTFILE ] ; then
|
||||
echo -e "Error: Input file $INPUTFILE does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -e $OUTPUTFILE ] ; then
|
||||
echo -e "Error: Output file $OUTPUTFILE already exists."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for word in $(cat "$INPUTFILE")
|
||||
do
|
||||
echo -en "\x$word" >> $OUTPUTFILE
|
||||
done
|
||||
|
||||
dd if=$OUTPUTFILE of="$SDCARD"
|
@ -1,47 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
usage() { echo "Usage: $0 [-h] [-b <path/to/buildroot>] [-d <path/to/device tree>]" 1>&2; exit 1; }
|
||||
|
||||
help() {
|
||||
echo "Usage: $0 [OPTIONS] <device>"
|
||||
echo " -b <path/to/buildroot> get images from given buildroot"
|
||||
echo " -d <device tree name> specify device tree to use"
|
||||
exit 0;
|
||||
}
|
||||
|
||||
# defaults
|
||||
imageDir=$RISCV/buildroot/output/images
|
||||
DEVICE_TREE=${imageDir}/wally-virt.dtb
|
||||
|
||||
# Process options and arguments. The following code grabs the single
|
||||
# sdcard device argument no matter where it is in the positional
|
||||
# parameters list.
|
||||
ARGS=()
|
||||
while [ $OPTIND -le "$#" ] ; do
|
||||
if getopts "hb:d:" arg ; then
|
||||
case "${arg}" in
|
||||
h) help
|
||||
;;
|
||||
b) BUILDROOT=${OPTARG}
|
||||
;;
|
||||
d) DEVICE_TREE=${OPTARG}
|
||||
;;
|
||||
esac
|
||||
else
|
||||
ARGS+=("${!OPTIND}")
|
||||
((OPTIND++))
|
||||
fi
|
||||
done
|
||||
|
||||
# File location variables
|
||||
imageDir=$BUILDROOT/output/images
|
||||
|
||||
tvDir=$RISCV/linux-testvectors
|
||||
tcpPort=1239
|
||||
|
||||
# QEMU Simulation
|
||||
qemu-system-riscv64 \
|
||||
-M virt -m 256M -dtb $DEVICE_TREE \
|
||||
-nographic \
|
||||
-bios $imageDir/fw_jump.elf -kernel $imageDir/Image -append "root=/dev/vda ro"
|
||||
-singlestep -rtc clock=vm -icount shift=0,align=off,sleep=on
|
@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
for index in {450..500};
|
||||
do
|
||||
instrs=$(($index*1000000))
|
||||
echo "y" | nice -n 5 ./genCheckpoint.sh $instrs
|
||||
done
|
@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
imageDir=$RISCV/buildroot/output/images
|
||||
tvDir=$RISCV/linux-testvectors
|
||||
tcpPort=1239
|
||||
|
||||
# QEMU Simulation
|
||||
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
|
||||
# > ./qemu-serial \
|
||||
# -gdb tcp::$tcpPort -S) \
|
||||
# & riscv64-unknown-elf-gdb -quiet \
|
||||
# -ex "set pagination off" \
|
||||
# -ex "set logging overwrite on" \
|
||||
# -ex "set logging redirect on" \
|
||||
# -ex "set confirm off" \
|
||||
# -ex "target extended-remote :$tcpPort" \
|
||||
# -ex "maintenance packet Qqemu.PhyMemMode:1" \
|
||||
# -ex "file $imageDir/vmlinux"
|
@ -1,74 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# disassembleBootTrace.py
|
||||
# David_Harris@hmc.edu 22 November 2023
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||
#
|
||||
# Reads boottrace.log and disassembles the machine code
|
||||
#
|
||||
|
||||
import csv
|
||||
import os
|
||||
import re
|
||||
|
||||
# read a file from sim/logs/boottrace.log and extract the second comma-separated field containing the instruction
|
||||
print("Reading boottrace.log")
|
||||
trace = []
|
||||
count = 0
|
||||
with open('../../sim/logs/boottrace.log') as f:
|
||||
reader = csv.reader(f, delimiter=',')
|
||||
for row in reader:
|
||||
trace.append(row)
|
||||
count = count + 1
|
||||
if count > 50000000:
|
||||
break
|
||||
f.close()
|
||||
|
||||
print("Disassembling boottrace.log instructions")
|
||||
# Write an assembly language file with the machine code
|
||||
with (open('boottrace.S', 'w')) as f:
|
||||
f.write('main:\n')
|
||||
for row in trace:
|
||||
instr = row[1]
|
||||
# scrape off leading white space from instr
|
||||
instr = instr.lstrip()
|
||||
# check if last character indicates an compressed or uncompressed instruction
|
||||
lastNibble = instr[-1]
|
||||
if (lastNibble == '3' or lastNibble == '7' or lastNibble == 'b' or lastNibble == 'f'):
|
||||
# uncompressed
|
||||
f.write('.word 0x' + instr + '\n')
|
||||
else:
|
||||
# compressed
|
||||
instr = instr[-4:]
|
||||
f.write('.hword 0x' + instr + '\n')
|
||||
f.close()
|
||||
|
||||
# Then assemble and disassemble the file
|
||||
os.system('riscv64-unknown-elf-gcc -march=rv64gqc_zba_zbb_zbc_zbs_zfh_zicboz_zicbop_zicbom -mabi=lp64d -c boottrace.S')
|
||||
os.system('riscv64-unknown-elf-objdump -D boottrace.o > boottrace.objdump')
|
||||
|
||||
# Patch disassembly back into boottrace
|
||||
print("Inserting disassembly into trace")
|
||||
dumpedLines = []
|
||||
with (open('boottrace.objdump', 'r')) as f:
|
||||
lines = f.readlines()
|
||||
f.close()
|
||||
lines = lines[7:] # skip header
|
||||
p = r'[^:]*:\s*(\S*)\s*(.*)'
|
||||
for line in lines:
|
||||
match = re.search(p, line)
|
||||
if (match):
|
||||
dump = [match.group(1), match.group(2)]
|
||||
dumpedLines.append(dump)
|
||||
|
||||
linenum = 0
|
||||
for i in range(len(trace)):
|
||||
row = trace[i]
|
||||
row.insert(2, dumpedLines[i][1])
|
||||
|
||||
# write trace back to csv file
|
||||
print("Writing trace back to boottrace_disasm.log")
|
||||
with (open('boottrace_disasm.log', 'w')) as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerows(trace)
|
||||
f.close()
|
@ -1,68 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys, os
|
||||
from functools import reduce
|
||||
|
||||
################
|
||||
# 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
|
||||
|
||||
def strip0x(num):
|
||||
return num[2:]
|
||||
|
||||
def stripZeroes(num):
|
||||
num = num.strip('0')
|
||||
if num=='':
|
||||
return '0'
|
||||
else:
|
||||
return num
|
||||
|
||||
#############
|
||||
# Main Code #
|
||||
#############
|
||||
print("Begin filtering traps down to just external interrupts.")
|
||||
|
||||
# Parse Args
|
||||
if len(sys.argv) != 2:
|
||||
sys.exit('Error filterTrapsToInterrupts.py expects 1 arg: <path_to_testvector_dir>')
|
||||
tvDir = sys.argv[1]+'/'
|
||||
trapsFilePath = tvDir+'traps.txt'
|
||||
if not os.path.exists(trapsFilePath):
|
||||
sys.exit('Error input file '+trapsFilePath+'not found')
|
||||
|
||||
with open(tvDir+'interrupts.txt', 'w') as interruptsFile:
|
||||
with open(trapsFilePath, 'r') as trapsFile:
|
||||
while True:
|
||||
trap = trapsFile.readline()
|
||||
if trap == '':
|
||||
break
|
||||
trapType = trap.split(' ')[-1]
|
||||
if ('interrupt' in trap) and (('external' in trapType) or ('m_timer' in trapType)): # no s_timer because that is not controlled by CLINT
|
||||
interruptsFile.write(trap) # overall line
|
||||
interruptsFile.write(trapsFile.readline()) # attempted instr count
|
||||
interruptsFile.write(trapsFile.readline()) # hart #
|
||||
interruptsFile.write(trapsFile.readline()) # asynchronous
|
||||
interruptsFile.write(trapsFile.readline()) # cause
|
||||
interruptsFile.write(trapsFile.readline()) # epc
|
||||
interruptsFile.write(trapsFile.readline()) # tval
|
||||
interruptsFile.write(trapsFile.readline()) # description
|
||||
else:
|
||||
for i in range(7):
|
||||
trapsFile.readline()
|
||||
|
||||
print("Finished filtering traps down to just external interrupts.")
|
@ -1,146 +0,0 @@
|
||||
#!/bin/bash
|
||||
tcpPort=1238
|
||||
imageDir=$RISCV/buildroot/output/images
|
||||
tvDir=$RISCV/linux-testvectors
|
||||
recordFile="$tvDir/all.qemu"
|
||||
traceFile="$tvDir/all.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"
|
||||
rawStateFile="$checkPtDir/stateGDB.txt"
|
||||
rawUartStateFile="$checkPtDir/uartStateGDB.txt"
|
||||
uartStateFile="$checkPtDir/checkpoint-UART"
|
||||
rawPlicStateFile="$checkPtDir/plicStateGDB.txt"
|
||||
plicStateFile="$checkPtDir/checkpoint-PLIC"
|
||||
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!"
|
||||
if [ ! -d "$tvDir" ]; then
|
||||
echo "Error: linux testvector directory $tvDir not found!">&2
|
||||
echo "Please create it. For example:">&2
|
||||
echo " sudo mkdir -p $tvDir">&2
|
||||
exit 1
|
||||
fi
|
||||
test -w $tvDir
|
||||
if [ ! $? -eq 0 ]; then
|
||||
echo "Error: insuffcient write privileges for linux testvector directory $tvDir !">&2
|
||||
echo "Please chmod it. For example:">&2
|
||||
echo " sudo chmod -R a+rw $tvDir">&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p $checkPtDir
|
||||
|
||||
# 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
|
||||
cat > genCheckpoint.gdb <<- end_of_script
|
||||
set pagination off
|
||||
set logging overwrite on
|
||||
set logging redirect on
|
||||
set confirm off
|
||||
target extended-remote :$tcpPort
|
||||
maintenance packet Qqemu.PhyMemMode:1
|
||||
file $imageDir/vmlinux
|
||||
# Step over reset vector into actual code
|
||||
stepi 100
|
||||
shell echo \"GDB proceeding to checkpoint at $instrs instrs, pc $pc\"
|
||||
b *0x$pc
|
||||
ignore 1 $occurences
|
||||
c
|
||||
shell echo \"Reached checkpoint at $instrs instrs\"
|
||||
shell echo \"GDB storing CPU state to $rawStateFile\"
|
||||
set logging file $rawStateFile
|
||||
set logging on
|
||||
info all-registers
|
||||
set logging off
|
||||
shell echo \"GDB storing UART state to $rawUartStateFile\"
|
||||
# Save value of LCR
|
||||
set \$LCR=*0x10000003 & 0xff
|
||||
set logging file $rawUartStateFile
|
||||
set logging on
|
||||
# Change LCR to set DLAB=0 to be able to read RBR and IER
|
||||
set {char}0x10000003 &= ~0x80
|
||||
x/1xb 0x10000000
|
||||
x/1xb 0x10000001
|
||||
x/1xb 0x10000002
|
||||
# But log original value of LCR
|
||||
printf "0x10000003:\t0x%02x\n", \$LCR
|
||||
x/1xb 0x10000004
|
||||
x/1xb 0x10000005
|
||||
x/1xb 0x10000006
|
||||
x/1xb 0x10000007
|
||||
set logging off
|
||||
shell echo \"GDB storing PLIC state to $rawPlicStateFile\"
|
||||
shell echo \"Note: this dumping assumes a maximum of 63 PLIC sources\"
|
||||
set logging file $rawPlicStateFile
|
||||
set logging on
|
||||
# Priority Levels for sources 1 thru 63
|
||||
x/63xw 0x0C000004
|
||||
# Interrupt Enables for sources 1 thru 63 for contexts 0 and 1
|
||||
x/2xw 0x0C002000
|
||||
x/2xw 0x0C002080
|
||||
# Global Priority Threshold for contexts 0 and 1
|
||||
x/1xw 0x0C200000
|
||||
x/1xw 0x0C201000
|
||||
set logging off
|
||||
shell echo \"GDB storing RAM to $rawRamFile\"
|
||||
dump binary memory $rawRamFile 0x80000000 0x87ffffff
|
||||
kill
|
||||
q
|
||||
end_of_script
|
||||
|
||||
# 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 \
|
||||
-gdb tcp::$tcpPort -S \
|
||||
1>./qemu-serial) \
|
||||
& riscv64-unknown-elf-gdb --quiet -x genCheckpoint.gdb
|
||||
|
||||
echo "Completed GDB script at $(date +%H:%M:%S)"
|
||||
|
||||
# Post-Process GDB outputs
|
||||
./parseState.py "$checkPtDir"
|
||||
./parseUartState.py "$checkPtDir"
|
||||
./parsePlicState.py "$checkPtDir"
|
||||
echo "Changing Endianness at $(date +%H:%M:%S)"
|
||||
make fixBinMem
|
||||
./fixBinMem "$rawRamFile" "$ramFile"
|
||||
echo "Copying over a truncated trace"
|
||||
tail -n+$instrs $traceFile > $outTraceFile
|
||||
|
||||
echo "Checkpoint completed at $(date +%H:%M:%S)"
|
||||
echo "You may want to restrict write access to $tvDir now and give cad ownership of it."
|
||||
echo "Run the following:"
|
||||
echo " sudo chown -R cad:cad $tvDir"
|
||||
echo " sudo chmod -R go-w $tvDir"
|
||||
fi
|
||||
|
@ -1,38 +0,0 @@
|
||||
#!/bin/bash
|
||||
imageDir=$RISCV/buildroot/output/images
|
||||
tvDir=$RISCV/linux-testvectors
|
||||
recordFile="$tvDir/all.qemu"
|
||||
DEVICE_TREE=${imageDir}/wally-virt.dtb
|
||||
|
||||
read -p "Warning: running this script will overwrite $recordFile
|
||||
Would you like to proceed? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
if [ ! -d "$tvDir" ]; then
|
||||
echo "Error: linux testvector directory $tvDir not found!">&2
|
||||
echo "Please create it. For example:">&2
|
||||
echo " sudo mkdir -p $tvDir">&2
|
||||
exit 1
|
||||
fi
|
||||
test -w $tvDir
|
||||
if [ ! $? -eq 0 ]; then
|
||||
echo "Error: insuffcient write privileges for linux testvector directory $tvDir !">&2
|
||||
echo "Please chmod it. For example:">&2
|
||||
echo " sudo chmod -R a+rw $tvDir">&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Launching QEMU in record mode!"
|
||||
qemu-system-riscv64 \
|
||||
-M virt -m 256M -dtb $DEVICE_TREE \
|
||||
-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=record,rrfile=$recordFile
|
||||
|
||||
echo "genRecording.sh completed!"
|
||||
echo "You may want to restrict write access to $tvDir now and give cad ownership of it."
|
||||
echo "Run the following:"
|
||||
echo " sudo chown -R cad:cad $tvDir"
|
||||
echo " sudo chmod -R go-w $tvDir"
|
||||
fi
|
@ -1,54 +0,0 @@
|
||||
#!/bin/bash
|
||||
tcpPort=1234
|
||||
imageDir=$RISCV/buildroot/output/images
|
||||
tvDir=$RISCV/linux-testvectors
|
||||
recordFile="$tvDir/all.qemu"
|
||||
traceFile="$tvDir/all.txt"
|
||||
trapsFile="$tvDir/traps.txt"
|
||||
interruptsFile="$tvDir/interrupts.txt"
|
||||
DEVICE_TREE=${imageDir}/wally-virt.dtb
|
||||
|
||||
read -p "Warning: running this script will overwrite the contents of:
|
||||
* $traceFile
|
||||
* $trapsFile
|
||||
* $interruptsFile
|
||||
Would you like to proceed? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]
|
||||
then
|
||||
if [ ! -d "$tvDir" ]; then
|
||||
echo "Error: linux testvector directory $tvDir not found!">&2
|
||||
echo "Please create it. For example:">&2
|
||||
echo " sudo mkdir -p $tvDir">&2
|
||||
exit 1
|
||||
fi
|
||||
test -w $tvDir
|
||||
if [ ! $? -eq 0 ]; then
|
||||
echo "Error: insuffcient write privileges for linux testvector directory $tvDir !">&2
|
||||
echo "Please chmod it. For example:">&2
|
||||
echo " sudo chmod -R a+rw $tvDir">&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
touch $traceFile
|
||||
touch $trapsFile
|
||||
touch $interruptsFile
|
||||
|
||||
# QEMU Simulation
|
||||
echo "Launching QEMU in replay mode!"
|
||||
(qemu-system-riscv64 \
|
||||
-M virt -m 256M -dtb $DEVICE_TREE \
|
||||
-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 \
|
||||
2>&1 >./qemu-serial | ./parseQEMUtoGDB.py | ./parseGDBtoTrace.py $trapsFile > $traceFile)
|
||||
|
||||
./filterTrapsToInterrupts.py $tvDir
|
||||
|
||||
echo "genTrace.sh completed!"
|
||||
echo "You may want to restrict write access to $tvDir now and give cad ownership of it."
|
||||
echo "Run the following:"
|
||||
echo " sudo chown -R cad:cad $tvDir"
|
||||
echo " sudo chmod -R go-w $tvDir"
|
||||
fi
|
@ -1,243 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys, fileinput, re
|
||||
|
||||
# Rose Thompson
|
||||
# July 27, 2021
|
||||
# Rewrite of the linux trace parser.
|
||||
|
||||
|
||||
InstrStartDelim = '=>'
|
||||
InstrEndDelim = '-----'
|
||||
|
||||
#InputFile = 'noparse.txt'
|
||||
#InputFile = sys.stdin
|
||||
#InputFile = 'temp.txt'
|
||||
#OutputFile = 'parsedAll.txt'
|
||||
|
||||
HUMAN_READABLE = False
|
||||
|
||||
def toDict(lst):
|
||||
'Converts the list of register values to a dictionary'
|
||||
dct= {}
|
||||
for item in lst:
|
||||
regTup = item.split()
|
||||
dct[regTup[0]] = int(regTup[2], 10)
|
||||
del dct['pc']
|
||||
return dct
|
||||
|
||||
def whichClass(text, Regs):
|
||||
'Which instruction class?'
|
||||
#print(text, Regs)
|
||||
if text[0:2] == 'ld' or text[0:2] == 'lw' or text[0:2] == 'lh' or text[0:2] == 'lb':
|
||||
return ('load', WhatAddr(text, Regs), None, WhatMemDestSource(text))
|
||||
elif text[0:2] == 'sd' or text[0:2] == 'sw' or text[0:2] == 'sh' or text[0:2] == 'sb':
|
||||
return ('store', WhatAddr(text, Regs), WhatMemDestSource(text), None)
|
||||
elif text[0:3] == 'amo':
|
||||
return ('amo', WhatAddrAMO(text, Regs), WhatMemDestSource(text), WhatMemDestSource(text))
|
||||
elif text[0:2] == 'lr':
|
||||
return ('lr', WhatAddrLR(text, Regs), None, WhatMemDestSource(text))
|
||||
elif text[0:2] == 'sc':
|
||||
return ('sc', WhatAddrSC(text, Regs), WhatMemDestSource(text), None)
|
||||
else:
|
||||
return ('other', None, None, None)
|
||||
|
||||
def whatChanged(dct0, dct1):
|
||||
'Compares two dictionaries of instrution registers and indicates which registers changed'
|
||||
dct = {}
|
||||
for key in dct0:
|
||||
if (dct1[key] != dct0[key]):
|
||||
dct[key] = dct1[key]
|
||||
return dct
|
||||
|
||||
def WhatMemDestSource(text):
|
||||
''''What is the destination register. Used to compute where the read data is
|
||||
on a load or the write data on a store.'''
|
||||
return text.split()[1].split(',')[0]
|
||||
|
||||
def WhatAddr(text, Regs):
|
||||
'What is the data memory address?'
|
||||
Imm = text.split(',')[1]
|
||||
(Imm, Src) = Imm.split('(')
|
||||
Imm = int(Imm.strip(), 10)
|
||||
Src = Src.strip(')').strip()
|
||||
RegVal = Regs[Src]
|
||||
return Imm + RegVal
|
||||
|
||||
def WhatAddrAMO(text, Regs):
|
||||
'What is the data memory address?'
|
||||
Src = text.split('(')[1]
|
||||
Src = Src.strip(')').strip()
|
||||
return Regs[Src]
|
||||
|
||||
def WhatAddrLR(text, Regs):
|
||||
'What is the data memory address?'
|
||||
Src = text.split('(')[1]
|
||||
Src = Src.strip(')').strip()
|
||||
return Regs[Src]
|
||||
|
||||
def WhatAddrSC(text, Regs):
|
||||
'What is the data memory address?'
|
||||
Src = text.split('(')[1]
|
||||
Src = Src.strip(')').strip()
|
||||
return Regs[Src]
|
||||
|
||||
def PrintInstr(instr):
|
||||
if instr[2] == None:
|
||||
return
|
||||
ChangedRegisters = instr[4]
|
||||
GPR = ''
|
||||
CSR = []
|
||||
for key in ChangedRegisters:
|
||||
# filter out csr which are not checked.
|
||||
if(key in RegNumber):
|
||||
if(RegNumber[key] < 32):
|
||||
# GPR
|
||||
if(HUMAN_READABLE):
|
||||
GPR = '{:-2d} {:016x}'.format(RegNumber[key], ChangedRegisters[key])
|
||||
else:
|
||||
GPR = '{:d} {:x}'.format(RegNumber[key], ChangedRegisters[key])
|
||||
else:
|
||||
if(HUMAN_READABLE):
|
||||
CSR.extend([key, '{:016x}'.format(ChangedRegisters[key])])
|
||||
else:
|
||||
CSR.extend([key, '{:x}'.format(ChangedRegisters[key])])
|
||||
|
||||
CSRStr = ' '.join(CSR)
|
||||
|
||||
#print(instr)
|
||||
|
||||
if (HUMAN_READABLE == True):
|
||||
outString='{:016x} {:08x} {:25s}'.format(instr[0], instr[1], instr[2])
|
||||
if(len(GPR) != 0):
|
||||
outString+=' GPR {}'.format(GPR)
|
||||
if(instr[3] == 'load' or instr[3] == 'lr'):
|
||||
outString+=' MemR {:016x} {:016x} {:016x}'.format(instr[5], 0, instr[7])
|
||||
if(instr[3] == 'store'):
|
||||
outString+='\t\t\t MemW {:016x} {:016x} {:016x}'.format(instr[5], instr[6], 0)
|
||||
if(len(CSR) != 0):
|
||||
outString+=' CSR {}'.format(CSRStr)
|
||||
else:
|
||||
outString='{:x} {:x} {:s}'.format(instr[0], instr[1], instr[2].replace(' ', '_'))
|
||||
if(len(GPR) != 0):
|
||||
outString+=' GPR {}'.format(GPR)
|
||||
if(instr[3] == 'load' or instr[3] == 'lr'):
|
||||
outString+=' MemR {:x} {:x} {:x}'.format(instr[5], 0, instr[7])
|
||||
if(instr[3] == 'store'):
|
||||
outString+=' MemW {:x} {:x} {:x}'.format(instr[5], instr[6], 0)
|
||||
if(len(CSR) != 0):
|
||||
outString+=' CSR {}'.format(CSRStr)
|
||||
outString+='\n'
|
||||
return outString
|
||||
|
||||
# =========
|
||||
# Main Code
|
||||
# =========
|
||||
# Parse argument for interrupt file
|
||||
if len(sys.argv) != 2:
|
||||
sys.exit('Error parseGDBtoTrace.py expects 1 arg:\n <interrupt filename>>')
|
||||
interruptFname = sys.argv[1]
|
||||
# reg number
|
||||
RegNumber = {'zero': 0, 'ra': 1, 'sp': 2, 'gp': 3, 'tp': 4, 't0': 5, 't1': 6, 't2': 7, 's0': 8, 's1': 9, 'a0': 10, 'a1': 11, 'a2': 12, 'a3': 13, 'a4': 14, 'a5': 15, 'a6': 16, 'a7': 17, 's2': 18, 's3': 19, 's4': 20, 's5': 21, 's6': 22, 's7': 23, 's8': 24, 's9': 25, 's10': 26, 's11': 27, 't3': 28, 't4': 29, 't5': 30, 't6': 31, 'mhartid': 32, 'mstatus': 33, 'mip': 34, 'mie': 35, 'mideleg': 36, 'medeleg': 37, 'mtvec': 38, 'stvec': 39, 'mepc': 40, 'sepc': 41, 'mcause': 42, 'scause': 43, 'mtval': 44, 'stval': 45, 'mscratch': 46, 'sscratch': 47, 'satp': 48}
|
||||
# initial state
|
||||
CurrentInstr = ['0', '0', None, 'other', {'zero': 0, 'ra': 0, 'sp': 0, 'gp': 0, 'tp': 0, 't0': 0, 't1': 0, 't2': 0, 's0': 0, 's1': 0, 'a0': 0, 'a1': 0, 'a2': 0, 'a3': 0, 'a4': 0, 'a5': 0, 'a6': 0, 'a7': 0, 's2': 0, 's3': 0, 's4': 0, 's5': 0, 's6': 0, 's7': 0, 's8': 0, 's9': 0, 's10': 0, 's11': 0, 't3': 0, 't4': 0, 't5': 0, 't6': 0, 'mhartid': 0, 'mstatus': 0, 'mip': 0, 'mie': 0, 'mideleg': 0, 'medeleg': 0, 'mtvec': 0, 'stvec': 0, 'mepc': 0, 'sepc': 0, 'mcause': 0, 'scause': 0, 'mtval': 0, 'stval': 0, 'mscratch': 0, 'sscratch': 0, 'satp': 0}, {}, None, None, None]
|
||||
|
||||
#with open (InputFile, 'r') as InputFileFP:
|
||||
#lines = InputFileFP.readlines()
|
||||
lineNum = 0
|
||||
StartLine = 0
|
||||
EndLine = 0
|
||||
numInstrs = 0
|
||||
#instructions = []
|
||||
MemAdr = 0
|
||||
lines = []
|
||||
interrupts=open(interruptFname,'w')
|
||||
interrupts.close()
|
||||
|
||||
prevInstrOutString=''
|
||||
currInstrOutString=''
|
||||
for line in fileinput.input('-'):
|
||||
if line.startswith('riscv_cpu_do_interrupt'):
|
||||
with open(interruptFname,'a') as interrupts:
|
||||
# Write line
|
||||
# Example line: hart:0, async:0, cause:0000000000000002, epc:0x0000000080008548, tval:0x0000000000000000, desc=illegal_instruction
|
||||
interrupts.write(line)
|
||||
# Write instruction count
|
||||
interrupts.write(str(numInstrs)+'\n')
|
||||
# Convert line to rows of info for easier Verilog parsing
|
||||
vals=line.strip('riscv_cpu_do_interrupt: ').strip('\n').split(',')
|
||||
vals=[val.split(':')[-1].strip(' ') for val in vals]
|
||||
vals=[val.split('=')[-1].strip(' ') for val in vals]
|
||||
for val in vals:
|
||||
interrupts.write(val+'\n')
|
||||
continue
|
||||
lines.insert(lineNum, line)
|
||||
if InstrStartDelim in line:
|
||||
lineNum = 0
|
||||
StartLine = lineNum
|
||||
elif InstrEndDelim in line:
|
||||
EndLine = lineNum
|
||||
(InstrBits, text) = lines[StartLine].split(':')
|
||||
InstrBits = int(InstrBits.strip('=> '), 16)
|
||||
text = text.strip()
|
||||
PC = int(lines[StartLine+1].split(':')[0][2:], 16)
|
||||
Regs = toDict(lines[StartLine+2:EndLine])
|
||||
(Class, Addr, WriteReg, ReadReg) = whichClass(text, Regs)
|
||||
#print("CWR", Class, WriteReg, ReadReg)
|
||||
PreviousInstr = CurrentInstr
|
||||
|
||||
Changed = whatChanged(PreviousInstr[4], Regs)
|
||||
|
||||
if (ReadReg !=None): ReadData = ReadReg
|
||||
else: ReadData = None
|
||||
|
||||
if (WriteReg !=None): WriteData = WriteReg
|
||||
else: WriteData = None
|
||||
|
||||
CurrentInstr = [PC, InstrBits, text, Class, Regs, Changed, Addr, WriteData, ReadData]
|
||||
|
||||
#print(CurrentInstr[0:4], PreviousInstr[5], CurrentInstr[6:7], PreviousInstr[8])
|
||||
|
||||
# pc, instrbits, text and class come from the last line.
|
||||
MoveInstrToRegWriteLst = PreviousInstr[0:4]
|
||||
# updated registers come from the current line.
|
||||
MoveInstrToRegWriteLst.append(CurrentInstr[5]) # destination regs
|
||||
# memory address if present comes from the last line.
|
||||
MoveInstrToRegWriteLst.append(PreviousInstr[6]) # MemAdrM
|
||||
# write data from the previous line
|
||||
#MoveInstrToRegWriteLst.append(PreviousInstr[7]) # WriteDataM
|
||||
|
||||
if (PreviousInstr[7] != None):
|
||||
MoveInstrToRegWriteLst.append(Regs[PreviousInstr[7]]) # WriteDataM
|
||||
else:
|
||||
MoveInstrToRegWriteLst.append(None)
|
||||
|
||||
# read data from the current line
|
||||
#MoveInstrToRegWriteLst.append(PreviousInstr[8]) # ReadDataM
|
||||
if (PreviousInstr[8] != None):
|
||||
MoveInstrToRegWriteLst.append(Regs[PreviousInstr[8]]) # ReadDataM
|
||||
else:
|
||||
MoveInstrToRegWriteLst.append(None)
|
||||
|
||||
lines.clear()
|
||||
#instructions.append(MoveInstrToRegWriteLst)
|
||||
|
||||
prevInstrOutString = currInstrOutString
|
||||
currInstrOutString = PrintInstr(MoveInstrToRegWriteLst)
|
||||
# Remove duplicates
|
||||
if (PreviousInstr[0] != CurrentInstr[0]) and (currInstrOutString != None):
|
||||
sys.stdout.write(currInstrOutString)
|
||||
numInstrs += 1
|
||||
if (numInstrs % 1e5 == 0):
|
||||
sys.stderr.write('GDB trace parser reached '+str(numInstrs/1.0e6)+' million instrs.\n')
|
||||
sys.stderr.flush()
|
||||
lineNum += 1
|
||||
|
||||
|
||||
#for instruction in instructions[1::]:
|
||||
|
||||
|
||||
#with open(OutputFile, 'w') as OutputFileFP:
|
||||
# print('opened file')
|
||||
|
||||
|
||||
|
@ -1,106 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys, os
|
||||
from functools import reduce
|
||||
|
||||
################
|
||||
# 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
|
||||
|
||||
def strip0x(num):
|
||||
return num[2:]
|
||||
|
||||
def stripZeroes(num):
|
||||
num = int(num,16)
|
||||
return hex(num)[2:]
|
||||
|
||||
#############
|
||||
# Main Code #
|
||||
#############
|
||||
print("Begin parsing PLIC state.")
|
||||
|
||||
# Parse Args
|
||||
if len(sys.argv) != 2:
|
||||
sys.exit('Error parsePlicState.py expects 1 arg: <path_to_checkpoint_dir>')
|
||||
outDir = sys.argv[1]+'/'
|
||||
rawPlicStateFile = outDir+'plicStateGDB.txt'
|
||||
if not os.path.exists(rawPlicStateFile):
|
||||
sys.exit('Error input file '+rawPlicStateFile+'not found')
|
||||
|
||||
with open(rawPlicStateFile, 'r') as rawPlicStateFile:
|
||||
plicIntPriorityArray = [] # iterates over number of different sources
|
||||
# 0x0C000004 thru 0x0C000010
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C000014 thru 0x0C000020
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C000024 thru 0x0C000030
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C000034 thru 0x0C000040
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C000044 thru 0x0C000050
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C000054 thru 0x0C000060
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C000064 thru 0x0C000070
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C000074 thru 0x0C000080
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C000084 thru 0x0C000090
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C000094 thru 0x0C0000a0
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C0000a4 thru 0x0C0000b0
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C0000b4 thru 0x0C0000c0
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C0000c4 thru 0x0C0000d0
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C0000d4 thru 0x0C0000e0
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C0000e4 thru 0x0C0000f0
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C0000f4 thru 0x0C0000fc
|
||||
plicIntPriorityArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
|
||||
plicIntEnableArray = [] # iterates over number of different contexts
|
||||
# 0x0C020000 thru 0x0C020004
|
||||
plicIntEnable = tokenize(rawPlicStateFile.readline())[1:]
|
||||
plicIntEnable = map(strip0x,plicIntEnable)
|
||||
plicIntEnableArray.append(reduce(lambda x,y: y+x,plicIntEnable))
|
||||
# 0x0C020080 thru 0x0C020084
|
||||
plicIntEnable = tokenize(rawPlicStateFile.readline())[1:]
|
||||
plicIntEnable = map(strip0x,plicIntEnable)
|
||||
plicIntEnableArray.append(reduce(lambda x,y: y+x,plicIntEnable))
|
||||
|
||||
plicIntPriorityThresholdArray = [] # iterates over number of different contexts
|
||||
# 0x0C200000
|
||||
plicIntPriorityThresholdArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
# 0x0C201000
|
||||
plicIntPriorityThresholdArray += tokenize(rawPlicStateFile.readline())[1:]
|
||||
|
||||
with open(outDir+'checkpoint-PLIC_INT_PRIORITY', 'w') as outFile:
|
||||
for word in plicIntPriorityArray:
|
||||
outFile.write(stripZeroes(word[2:])+'\n')
|
||||
with open(outDir+'checkpoint-PLIC_INT_ENABLE', 'w') as outFile:
|
||||
for word in plicIntEnableArray:
|
||||
outFile.write(stripZeroes(word[2:])+'\n')
|
||||
with open(outDir+'checkpoint-PLIC_THRESHOLD', 'w') as outFile:
|
||||
for word in plicIntPriorityThresholdArray:
|
||||
outFile.write(stripZeroes(word[2:])+'\n')
|
||||
|
||||
print("Finished parsing PLIC state!")
|
@ -1,145 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import fileinput, sys
|
||||
|
||||
parseState = "idle"
|
||||
beginPageFault = 0
|
||||
inPageFault = 0
|
||||
endPageFault = 0
|
||||
CSRs = {}
|
||||
pageFaultCSRs = {}
|
||||
regs = {}
|
||||
pageFaultRegs = {}
|
||||
instrs = {}
|
||||
instrCount = 0
|
||||
returnAdr = 0
|
||||
sys.stderr.write("reminder: parse_qemu.py takes input from stdin\n")
|
||||
|
||||
def printPC(l):
|
||||
global parseState, inPageFault, CSRs, pageFaultCSRs, regs, pageFaultCSRs, instrs, instrCount
|
||||
if not inPageFault:
|
||||
inst = l.split()
|
||||
if len(inst) > 3:
|
||||
print(f'=> {inst[1]}:\t{inst[2]} {inst[3]}')
|
||||
else:
|
||||
print(f'=> {inst[1]}:\t{inst[2]}')
|
||||
print(f'{inst[0]} 0x{inst[1]}')
|
||||
instrCount += 1
|
||||
if ((instrCount % 100000) == 0):
|
||||
sys.stderr.write("QEMU parser reached "+str(instrCount)+" instrs\n")
|
||||
|
||||
def printCSRs():
|
||||
global parseState, inPageFault, CSRs, pageFaultCSRs, regs, pageFaultCSRs, instrs
|
||||
global interrupt_line
|
||||
if not inPageFault:
|
||||
for (csr,val) in CSRs.items():
|
||||
print('{}{}{:#x} {}'.format(csr, ' '*(15-len(csr)), val, val))
|
||||
print('-----') # end of current instruction
|
||||
if len(interrupt_line)>0: # squish interrupts in between instructions
|
||||
print(interrupt_line)
|
||||
interrupt_line=""
|
||||
|
||||
def parseCSRs(l):
|
||||
global parseState, inPageFault, CSRs, pageFaultCSRs, regs, pageFaultCSRs, instrs
|
||||
if l.strip() and (not l.startswith("Disassembler")) and (not l.startswith("Please")):
|
||||
# If we've hit the register file
|
||||
if l.startswith(' x0/zero'):
|
||||
parseState = "regFile"
|
||||
if not inPageFault:
|
||||
instr = instrs[CSRs["pc"]]
|
||||
printPC(instr)
|
||||
parseRegs(l)
|
||||
# If we've hit a CSR
|
||||
else:
|
||||
csr = l.split()[0]
|
||||
val = int(l.split()[1],16)
|
||||
# Commented out this conditional because the pageFault instrs don't corrupt CSRs
|
||||
#if inPageFault:
|
||||
# Not sure if these CSRs should be updated or not during page fault.
|
||||
#if l.startswith("mstatus") or l.startswith("mepc") or l.startswith("mcause") or l.startswith("mtval") or l.startswith("sepc") or l.startswith("scause") or l.startswith("stval"):
|
||||
# We do update some CSRs
|
||||
# CSRs[csr] = val
|
||||
#else:
|
||||
# Others we preserve until changed later
|
||||
# pageFaultCSRs[csr] = val
|
||||
#elif pageFaultCSRs and (csr in pageFaultCSRs):
|
||||
# if (val != pageFaultCSRs[csr]):
|
||||
# del pageFaultCSRs[csr]
|
||||
# CSRs[csr] = val
|
||||
#else:
|
||||
# CSRs[csr] = val
|
||||
#
|
||||
# However SEPC and STVAL do get corrupted upon exiting
|
||||
if endPageFault and ((csr == 'sepc') or (csr == 'stval')):
|
||||
CSRs[csr] = returnAdr
|
||||
pageFaultCSRs[csr] = val
|
||||
elif pageFaultCSRs and (csr in pageFaultCSRs):
|
||||
if (val != pageFaultCSRs[csr]):
|
||||
del pageFaultCSRs[csr]
|
||||
CSRs[csr] = val
|
||||
else:
|
||||
CSRs[csr] = val
|
||||
|
||||
def parseRegs(l):
|
||||
global parseState, inPageFault, CSRs, pageFaultCSRs, regs, pageFaultCSRs, instrs, pageFaultRegs
|
||||
if "pc" in l:
|
||||
printCSRs()
|
||||
# New non-disassembled instruction
|
||||
parseState = "CSRs"
|
||||
parseCSRs(l)
|
||||
elif l.startswith('--------'):
|
||||
# End of disassembled instruction
|
||||
printCSRs()
|
||||
parseState = "idle"
|
||||
else:
|
||||
s = l.split()
|
||||
for i in range(0,len(s),2):
|
||||
if '/' in s[i]:
|
||||
reg = s[i].split('/')[1]
|
||||
val = int(s[i+1], 16)
|
||||
if inPageFault:
|
||||
pageFaultRegs[reg] = val
|
||||
else:
|
||||
if pageFaultRegs and (reg in pageFaultRegs):
|
||||
if (val != pageFaultRegs[reg]):
|
||||
del pageFaultRegs[reg]
|
||||
regs[reg] = val
|
||||
else:
|
||||
regs[reg] = val
|
||||
val = regs[reg]
|
||||
print('{}{}{:#x} {}'.format(reg, ' '*(15-len(reg)), val, val))
|
||||
else:
|
||||
sys.stderr.write("Whoops. Expected a list of reg file regs; got:\n"+l)
|
||||
|
||||
#############
|
||||
# Main Code #
|
||||
#############
|
||||
interrupt_line=""
|
||||
for l in fileinput.input():
|
||||
#sys.stderr.write(l)
|
||||
if l.startswith('riscv_cpu_do_interrupt'):
|
||||
sys.stderr.write(l)
|
||||
interrupt_line = l.strip('\n')
|
||||
elif l.startswith('qemu-system-riscv64: QEMU: Terminated via GDBstub'):
|
||||
break
|
||||
elif l.startswith('IN:'):
|
||||
# New disassembled instr
|
||||
parseState = "instr"
|
||||
elif (parseState == "instr") and l.startswith('0x'):
|
||||
# New instruction
|
||||
if "out of bounds" in l:
|
||||
sys.stderr.write("Detected QEMU page fault error\n")
|
||||
beginPageFault = not inPageFault
|
||||
if beginPageFault:
|
||||
returnAdr = int(l.split()[0][2:-1], 16)
|
||||
sys.stderr.write('Saving SEPC of '+hex(returnAdr)+'\n')
|
||||
inPageFault = 1
|
||||
else:
|
||||
endPageFault = inPageFault
|
||||
inPageFault = 0
|
||||
adr = int(l.split()[0][2:-1], 16)
|
||||
instrs[adr] = l
|
||||
parseState = "CSRs"
|
||||
elif parseState == "CSRs":
|
||||
parseCSRs(l)
|
||||
elif parseState == "regFile":
|
||||
parseRegs(l)
|
@ -1,99 +0,0 @@
|
||||
#!/usr/bin/env 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 CPU 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','sie','sip','sstatus']
|
||||
# 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 CPU state!")
|
@ -1,50 +0,0 @@
|
||||
#!/usr/bin/env 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 UART state.")
|
||||
|
||||
# Parse Args
|
||||
if len(sys.argv) != 2:
|
||||
sys.exit('Error parseUartState.py expects 1 arg: <path_to_checkpoint_dir>')
|
||||
outDir = sys.argv[1]+'/'
|
||||
rawUartStateFile = outDir+'uartStateGDB.txt'
|
||||
if not os.path.exists(rawUartStateFile):
|
||||
sys.exit('Error input file '+rawUartStateFile+'not found')
|
||||
|
||||
with open(rawUartStateFile, 'r') as rawUartStateFile:
|
||||
uartBytes = []
|
||||
for i in range(0,8):
|
||||
uartBytes += tokenize(rawUartStateFile.readline())[1:]
|
||||
with open(outDir+'checkpoint-UART_IER', 'w') as outFile:
|
||||
outFile.write(uartBytes[1][2:])
|
||||
with open(outDir+'checkpoint-UART_LCR', 'w') as outFile:
|
||||
outFile.write(uartBytes[3][2:])
|
||||
with open(outDir+'checkpoint-UART_MCR', 'w') as outFile:
|
||||
outFile.write(uartBytes[4][2:])
|
||||
with open(outDir+'checkpoint-UART_SCR', 'w') as outFile:
|
||||
outFile.write(uartBytes[7][2:])
|
||||
|
||||
print("Finished parsing UART state!")
|
@ -181,7 +181,7 @@ if {$DEBUG > 0} {
|
||||
# suppress spurious warnngs about
|
||||
# "Extra checking for conflicts with always_comb done at vopt time"
|
||||
# because vsim will run vopt
|
||||
set INC_DIRS "+incdir+${CONFIG}/${CFG} +incdir+${CONFIG}/deriv/${CFG} +incdir+${CONFIG}/shared +incdir+${FCRVVI} +incdir+${FCRVVI}/rv32 +incdir+${FCRVVI}/rv64 +incdir+${FCRVVI}/rv64_priv +incdir+${FCRVVI}/priv +incdir+${FCRVVI}/common +incdir+${FCRVVI}"
|
||||
set INC_DIRS "+incdir+${CONFIG}/${CFG} +incdir+${CONFIG}/deriv/${CFG} +incdir+${CONFIG}/shared +incdir+${FCRVVI} +incdir+${FCRVVI}/rv32 +incdir+${FCRVVI}/rv64 +incdir+${FCRVVI}/rv64_priv +incdir+${FCRVVI}/priv +incdir+${FCRVVI}/rv32_priv +incdir+${FCRVVI}/common +incdir+${FCRVVI}"
|
||||
set SOURCES "${SRC}/cvw.sv ${TB}/${TESTBENCH}.sv ${TB}/common/*.sv ${SRC}/*/*.sv ${SRC}/*/*/*.sv ${WALLY}/addins/verilog-ethernet/*/*.sv ${WALLY}/addins/verilog-ethernet/*/*/*/*.sv"
|
||||
vlog -permissive -lint -work ${WKDIR} {*}${INC_DIRS} {*}${FCvlog} {*}${FCdefineCOVER_EXTS} {*}${lockstepvlog} {*}${SOURCES} -suppress 2282,2583,7053,7063,2596,13286
|
||||
|
||||
|
@ -126,7 +126,7 @@ module fround import cvw::*; #(parameter cvw_t P) (
|
||||
3'b001: RoundUp = 0; // RZ
|
||||
3'b010: RoundUp = Xs & (Rp | Tp); // RN
|
||||
3'b011: RoundUp = ~Xs & (Rp | Tp); // RP
|
||||
3'b101: RoundUp = Rp; // RNTA
|
||||
3'b100: RoundUp = Rp; // RNTA
|
||||
default: RoundUp = 0; // should never happen
|
||||
endcase
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
///////////////////////////////////////////
|
||||
// spi_apb.sv
|
||||
//
|
||||
// Written: Naiche Whyte-Aguayo nwhyteaguayo@g.hmc.edu 11/16/2022
|
||||
// Written: Naiche Whyte-Aguayo nwhyteaguayo@g.hmc.edu
|
||||
// Jacob Pease jacobpease@protonmail.com (October 29th, 2024)
|
||||
// Created: November 16th, 2022
|
||||
//
|
||||
// Purpose: SPI peripheral
|
||||
//
|
||||
@ -13,7 +15,7 @@
|
||||
//
|
||||
// A component of the Wally configurable RISC-V project.
|
||||
//
|
||||
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
|
||||
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||
//
|
||||
@ -30,438 +32,324 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
module spi_apb import cvw::*; #(parameter cvw_t P) (
|
||||
input logic PCLK, PRESETn,
|
||||
input logic PSEL,
|
||||
input logic [7:0] PADDR,
|
||||
input logic [P.XLEN-1:0] PWDATA,
|
||||
input logic [P.XLEN/8-1:0] PSTRB,
|
||||
input logic PWRITE,
|
||||
input logic PENABLE,
|
||||
output logic PREADY,
|
||||
output logic [P.XLEN-1:0] PRDATA,
|
||||
output logic SPIOut,
|
||||
input logic SPIIn,
|
||||
output logic [3:0] SPICS,
|
||||
output logic SPIIntr,
|
||||
output logic SPICLK
|
||||
input logic PCLK, PRESETn,
|
||||
input logic PSEL,
|
||||
input logic [7:0] PADDR,
|
||||
input logic [P.XLEN-1:0] PWDATA,
|
||||
input logic [P.XLEN/8-1:0] PSTRB,
|
||||
input logic PWRITE,
|
||||
input logic PENABLE,
|
||||
output logic PREADY,
|
||||
output logic [P.XLEN-1:0] PRDATA,
|
||||
output logic SPIOut,
|
||||
input logic SPIIn,
|
||||
output logic [3:0] SPICS,
|
||||
output logic SPIIntr,
|
||||
output logic SPICLK
|
||||
);
|
||||
|
||||
// register map
|
||||
localparam SPI_SCKDIV = 8'h00;
|
||||
localparam SPI_SCKMODE = 8'h04;
|
||||
localparam SPI_CSID = 8'h10;
|
||||
localparam SPI_CSDEF = 8'h14;
|
||||
localparam SPI_CSMODE = 8'h18;
|
||||
localparam SPI_DELAY0 = 8'h28;
|
||||
localparam SPI_DELAY1 = 8'h2C;
|
||||
localparam SPI_FMT = 8'h40;
|
||||
localparam SPI_TXDATA = 8'h48;
|
||||
localparam SPI_RXDATA = 8'h4C;
|
||||
localparam SPI_TXMARK = 8'h50;
|
||||
localparam SPI_RXMARK = 8'h54;
|
||||
localparam SPI_IE = 8'h70;
|
||||
localparam SPI_IP = 8'h74;
|
||||
|
||||
// receive shift register states
|
||||
typedef enum logic [1:0] {ReceiveShiftFullState, ReceiveShiftNotFullState, ReceiveShiftDelayState} rsrstatetype;
|
||||
|
||||
|
||||
// SPI control registers. Refer to SiFive FU540-C000 manual
|
||||
logic [11:0] SckDiv;
|
||||
logic [1:0] SckMode;
|
||||
logic [1:0] ChipSelectID;
|
||||
logic [3:0] ChipSelectDef;
|
||||
logic [1:0] ChipSelectMode;
|
||||
logic [15:0] Delay0, Delay1;
|
||||
logic [4:0] Format;
|
||||
logic [7:0] ReceiveData;
|
||||
logic [2:0] TransmitWatermark, ReceiveWatermark;
|
||||
logic [8:0] TransmitData;
|
||||
logic [1:0] InterruptEnable, InterruptPending;
|
||||
|
||||
// Bus interface signals
|
||||
logic [7:0] Entry;
|
||||
logic Memwrite;
|
||||
logic [31:0] Din, Dout;
|
||||
logic TransmitInactive; // High when there is no transmission, used as hardware interlock signal
|
||||
|
||||
// FIFO FSM signals
|
||||
// Watermark signals - TransmitReadMark = ip[0], ReceiveWriteMark = ip[1]
|
||||
logic TransmitWriteMark, TransmitReadMark, RecieveWriteMark, RecieveReadMark;
|
||||
logic TransmitFIFOWriteFull, TransmitFIFOReadEmpty;
|
||||
logic TransmitFIFOWriteIncrement;
|
||||
logic ReceiveFiFoWriteInc;
|
||||
logic ReceiveFIFOReadIncrement;
|
||||
logic ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty;
|
||||
logic [7:0] TransmitFIFOReadData;
|
||||
/* verilator lint_off UNDRIVEN */
|
||||
logic [2:0] TransmitWriteWatermarkLevel, ReceiveReadWatermarkLevel; // unused generic FIFO outputs
|
||||
/* verilator lint_off UNDRIVEN */
|
||||
logic [7:0] ReceiveShiftRegEndian; // Reverses ReceiveShiftReg if Format[2] set (little endian transmission)
|
||||
rsrstatetype ReceiveState;
|
||||
logic ReceiveFiFoTakingData;
|
||||
|
||||
// Transmission signals
|
||||
logic ZeroDiv; // High when SckDiv is 0
|
||||
logic [11:0] DivCounter; // Counter for sck
|
||||
logic SCLKenable; // Flip flop enable high every sclk edge
|
||||
|
||||
// Delay signals
|
||||
logic [8:0] ImplicitDelay1; // Adds implicit delay to cs-sck delay counter based on phase
|
||||
logic [8:0] ImplicitDelay2; // Adds implicit delay to sck-cs delay counter based on phase
|
||||
logic [8:0] CS_SCKCount; // Counter for cs-sck delay
|
||||
logic [8:0] SCK_CSCount; // Counter for sck-cs delay
|
||||
logic [8:0] InterCSCount; // Counter for inter cs delay
|
||||
logic [8:0] InterXFRCount; // Counter for inter xfr delay
|
||||
logic ZeroDelayHoldMode; // High when ChipSelectMode is hold and Delay1[15:8] (InterXFR delay) is 0
|
||||
|
||||
// Frame counting signals
|
||||
logic FirstFrame;
|
||||
logic [3:0] FrameCount; // Counter for number of frames in transmission
|
||||
logic ReceivePenultimateFrame; // High when penultimate frame in transmission has been reached
|
||||
|
||||
// State fsm signals
|
||||
logic Active; // High when state is either Active1 or Active0 (during transmission)
|
||||
logic Active0; // High when state is Active0
|
||||
|
||||
// Shift reg signals
|
||||
logic ShiftEdge; // Determines which edge of sck to shift from TransmitShiftReg
|
||||
logic [7:0] TransmitShiftReg; // Transmit shift register
|
||||
logic [7:0] ReceiveShiftReg; // Receive shift register
|
||||
logic SampleEdge; // Determines which edge of sck to sample from ReceiveShiftReg
|
||||
logic [7:0] TransmitDataEndian; // Reverses TransmitData from txFIFO if littleendian, since TransmitReg always shifts MSB
|
||||
logic TransmitShiftRegLoad; // Determines when to load TransmitShiftReg
|
||||
logic TransmitShiftRegLoadSingleCycle; // Version of TransmitShiftRegLoad which is only high for a single SCLK cycle to prevent double loads
|
||||
logic TransmitShiftRegLoadDelay; // TransmitShiftRegLoad delayed by an SCLK cycle, inverted and anded with TransmitShiftRegLoad to create a single cycle signal
|
||||
logic TransmitFIFOReadIncrement; // Increments Tx FIFO read ptr 1 cycle after Tx FIFO is read
|
||||
logic ReceiveShiftFull; // High when receive shift register is full
|
||||
logic TransmitShiftEmpty; // High when transmit shift register is empty
|
||||
logic ShiftIn; // Determines whether to shift from SPIIn or SPIOut (if SPI_LOOPBACK_TEST)
|
||||
logic [3:0] LeftShiftAmount; // Determines left shift amount to left-align data when little endian
|
||||
logic [7:0] ASR; // AlignedReceiveShiftReg
|
||||
logic ShiftEdgeSPICLK; // Changes ShiftEdge when SckDiv is 0
|
||||
|
||||
// CS signals
|
||||
logic [3:0] ChipSelectAuto; // Assigns ChipSelect value to selected CS signal based on CS ID
|
||||
logic [3:0] ChipSelectInternal; // Defines what each ChipSelect signal should be based on transmission status and ChipSelectDef
|
||||
logic DelayMode; // Determines where to place implicit half cycle delay based on sck phase for CS assertion
|
||||
|
||||
// Miscellaneous signals delayed/early by 1 PCLK cycle
|
||||
logic ReceiveShiftFullDelay; // Delays ReceiveShiftFull signal by 1 PCLK cycle
|
||||
logic ReceiveShiftFullDelayPCLK; // ReceiveShiftFull delayed by 1 PCLK cycle
|
||||
logic TransmitFIFOReadEmptyDelay;
|
||||
logic SCLKenableEarly; // SCLKenable 1 PCLK cycle early, needed for on time register changes when ChipSelectMode is hold and Delay1[15:8] (InterXFR delay) is 0
|
||||
|
||||
|
||||
|
||||
// APB access
|
||||
assign Entry = {PADDR[7:2],2'b00}; // 32-bit word-aligned accesses
|
||||
assign Memwrite = PWRITE & PENABLE & PSEL; // Only write in access phase
|
||||
assign PREADY = Entry == SPI_TXDATA | Entry == SPI_RXDATA | Entry == SPI_IP | TransmitInactive; // Tie PREADY to transmission for hardware interlock
|
||||
|
||||
// Account for subword read/write circuitry
|
||||
// -- Note SPI registers are 32 bits no matter what; access them with LW SW.
|
||||
|
||||
assign Din = PWDATA[31:0];
|
||||
if (P.XLEN == 64) assign PRDATA = { Dout, Dout};
|
||||
else assign PRDATA = Dout;
|
||||
|
||||
// Register access
|
||||
always_ff@(posedge PCLK)
|
||||
if (~PRESETn) begin
|
||||
SckDiv <= 12'd3;
|
||||
SckMode <= 2'b0;
|
||||
ChipSelectID <= 2'b0;
|
||||
ChipSelectDef <= 4'b1111;
|
||||
ChipSelectMode <= 2'b0;
|
||||
Delay0 <= {8'b1,8'b1};
|
||||
Delay1 <= {8'b0,8'b1};
|
||||
Format <= {5'b10000};
|
||||
TransmitData <= 9'b0;
|
||||
TransmitWatermark <= 3'b0;
|
||||
ReceiveWatermark <= 3'b0;
|
||||
InterruptEnable <= 2'b0;
|
||||
InterruptPending <= 2'b0;
|
||||
end else begin // writes
|
||||
|
||||
|
||||
/* verilator lint_off CASEINCOMPLETE */
|
||||
if (Memwrite & TransmitInactive)
|
||||
case(Entry) // flop to sample inputs
|
||||
SPI_SCKDIV: SckDiv <= Din[11:0];
|
||||
SPI_SCKMODE: SckMode <= Din[1:0];
|
||||
SPI_CSID: ChipSelectID <= Din[1:0];
|
||||
SPI_CSDEF: ChipSelectDef <= Din[3:0];
|
||||
SPI_CSMODE: ChipSelectMode <= Din[1:0];
|
||||
SPI_DELAY0: Delay0 <= {Din[23:16], Din[7:0]};
|
||||
SPI_DELAY1: Delay1 <= {Din[23:16], Din[7:0]};
|
||||
SPI_FMT: Format <= {Din[19:16], Din[2]};
|
||||
SPI_TXMARK: TransmitWatermark <= Din[2:0];
|
||||
SPI_RXMARK: ReceiveWatermark <= Din[2:0];
|
||||
SPI_IE: InterruptEnable <= Din[1:0];
|
||||
endcase
|
||||
|
||||
if (Memwrite)
|
||||
case(Entry)
|
||||
SPI_TXDATA: if (~TransmitFIFOWriteFull) TransmitData[7:0] <= Din[7:0];
|
||||
endcase
|
||||
/* verilator lint_off CASEINCOMPLETE */
|
||||
|
||||
// According to FU540 spec: Once interrupt is pending, it will remain set until number
|
||||
// of entries in tx/rx fifo is strictly more/less than tx/rxmark
|
||||
InterruptPending[0] <= TransmitReadMark;
|
||||
InterruptPending[1] <= RecieveWriteMark;
|
||||
|
||||
case(Entry) // Flop to sample inputs
|
||||
SPI_SCKDIV: Dout <= {20'b0, SckDiv};
|
||||
SPI_SCKMODE: Dout <= {30'b0, SckMode};
|
||||
SPI_CSID: Dout <= {30'b0, ChipSelectID};
|
||||
SPI_CSDEF: Dout <= {28'b0, ChipSelectDef};
|
||||
SPI_CSMODE: Dout <= {30'b0, ChipSelectMode};
|
||||
SPI_DELAY0: Dout <= {8'b0, Delay0[15:8], 8'b0, Delay0[7:0]};
|
||||
SPI_DELAY1: Dout <= {8'b0, Delay1[15:8], 8'b0, Delay1[7:0]};
|
||||
SPI_FMT: Dout <= {12'b0, Format[4:1], 13'b0, Format[0], 2'b0};
|
||||
SPI_TXDATA: Dout <= {TransmitFIFOWriteFull, 23'b0, 8'b0};
|
||||
SPI_RXDATA: Dout <= {ReceiveFIFOReadEmpty, 23'b0, ReceiveData[7:0]};
|
||||
SPI_TXMARK: Dout <= {29'b0, TransmitWatermark};
|
||||
SPI_RXMARK: Dout <= {29'b0, ReceiveWatermark};
|
||||
SPI_IE: Dout <= {30'b0, InterruptEnable};
|
||||
SPI_IP: Dout <= {30'b0, InterruptPending};
|
||||
default: Dout <= 32'b0;
|
||||
endcase
|
||||
end
|
||||
|
||||
// SPI enable generation, where SCLK = PCLK/(2*(SckDiv + 1))
|
||||
// Asserts SCLKenable at the rising and falling edge of SCLK by counting from 0 to SckDiv
|
||||
// Active at 2x SCLK frequency to account for implicit half cycle delays and actions on both clock edges depending on phase
|
||||
// When SckDiv is 0, count doesn't work and SCLKenable is simply PCLK *** dh 10/26/24: this logic is seriously broken. SCLK is not scaled to PCLK/(2*(SckDiv + 1)). SCLKenableEarly doesn't work right for SckDiv=0
|
||||
assign ZeroDiv = ~|(SckDiv[10:0]);
|
||||
assign SCLKenable = ZeroDiv ? 1 : (DivCounter == SckDiv);
|
||||
assign SCLKenableEarly = ((DivCounter + 12'b1) == SckDiv);
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) DivCounter <= '0;
|
||||
else if (SCLKenable) DivCounter <= 12'b0;
|
||||
else DivCounter <= DivCounter + 12'b1;
|
||||
|
||||
// Asserts when transmission is one frame before complete
|
||||
assign ReceivePenultimateFrame = ((FrameCount + 4'b0001) == Format[4:1]);
|
||||
assign FirstFrame = (FrameCount == 4'b0);
|
||||
|
||||
// Computing delays
|
||||
// When sckmode.pha = 0, an extra half-period delay is implicit in the cs-sck delay, and vice-versa for sck-cs
|
||||
assign ImplicitDelay1 = SckMode[0] ? 9'b0 : 9'b1;
|
||||
assign ImplicitDelay2 = SckMode[0] ? 9'b1 : 9'b0;
|
||||
|
||||
// Calculate when tx/rx shift registers are full/empty
|
||||
|
||||
// Transmit Shift FSM
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) TransmitShiftEmpty <= 1'b1;
|
||||
else if (TransmitShiftEmpty) begin
|
||||
if (TransmitFIFOReadEmpty | (~TransmitFIFOReadEmpty & (ReceivePenultimateFrame & Active0))) TransmitShiftEmpty <= 1'b1;
|
||||
else if (~TransmitFIFOReadEmpty) TransmitShiftEmpty <= 1'b0;
|
||||
end else begin
|
||||
if (ReceivePenultimateFrame & Active0) TransmitShiftEmpty <= 1'b1;
|
||||
else TransmitShiftEmpty <= 1'b0;
|
||||
end
|
||||
|
||||
// Receive Shift FSM
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) ReceiveState <= ReceiveShiftNotFullState;
|
||||
else if (SCLKenable) begin
|
||||
case (ReceiveState)
|
||||
ReceiveShiftFullState: ReceiveState <= ReceiveShiftNotFullState;
|
||||
ReceiveShiftNotFullState: if (ReceivePenultimateFrame & (SampleEdge)) ReceiveState <= ReceiveShiftDelayState;
|
||||
else ReceiveState <= ReceiveShiftNotFullState;
|
||||
ReceiveShiftDelayState: ReceiveState <= ReceiveShiftFullState;
|
||||
endcase
|
||||
end
|
||||
|
||||
assign ReceiveShiftFull = SckMode[0] ? (ReceiveState == ReceiveShiftFullState) : (ReceiveState == ReceiveShiftDelayState);
|
||||
|
||||
// Calculate tx/rx fifo write and recieve increment signals
|
||||
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) TransmitFIFOWriteIncrement <= 1'b0;
|
||||
else TransmitFIFOWriteIncrement <= (Memwrite & (Entry == SPI_TXDATA) & ~TransmitFIFOWriteFull);
|
||||
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) ReceiveFIFOReadIncrement <= 1'b0;
|
||||
else ReceiveFIFOReadIncrement <= ((Entry == SPI_RXDATA) & ~ReceiveFIFOReadEmpty & PSEL & ~ReceiveFIFOReadIncrement);
|
||||
|
||||
assign TransmitShiftRegLoad = ~TransmitShiftEmpty & ~Active | (((ChipSelectMode == 2'b10) & ~|(Delay1[15:8])) & ((ReceiveShiftFullDelay | ReceiveShiftFull) & ~SampleEdge & ~TransmitFIFOReadEmpty));
|
||||
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) TransmitShiftRegLoadDelay <=0;
|
||||
else if (SCLKenable) TransmitShiftRegLoadDelay <= TransmitShiftRegLoad;
|
||||
assign TransmitShiftRegLoadSingleCycle = TransmitShiftRegLoad & ~TransmitShiftRegLoadDelay;
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) TransmitFIFOReadIncrement <= 0;
|
||||
else if (SCLKenable) TransmitFIFOReadIncrement <= TransmitShiftRegLoadSingleCycle;
|
||||
// Tx/Rx FIFOs
|
||||
spi_fifo #(3,8) txFIFO(PCLK, 1'b1, SCLKenable, PRESETn, TransmitFIFOWriteIncrement, TransmitFIFOReadIncrement, TransmitData[7:0], TransmitWriteWatermarkLevel, TransmitWatermark[2:0],
|
||||
TransmitFIFOReadData[7:0], TransmitFIFOWriteFull, TransmitFIFOReadEmpty, TransmitWriteMark, TransmitReadMark);
|
||||
spi_fifo #(3,8) rxFIFO(PCLK, SCLKenable, 1'b1, PRESETn, ReceiveFiFoWriteInc, ReceiveFIFOReadIncrement, ReceiveShiftRegEndian, ReceiveWatermark[2:0], ReceiveReadWatermarkLevel,
|
||||
ReceiveData[7:0], ReceiveFIFOWriteFull, ReceiveFIFOReadEmpty, RecieveWriteMark, RecieveReadMark);
|
||||
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) TransmitFIFOReadEmptyDelay <= 1'b1;
|
||||
else if (SCLKenable) TransmitFIFOReadEmptyDelay <= TransmitFIFOReadEmpty;
|
||||
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) ReceiveShiftFullDelay <= 1'b0;
|
||||
else if (SCLKenable) ReceiveShiftFullDelay <= ReceiveShiftFull;
|
||||
|
||||
assign ReceiveFiFoTakingData = ReceiveFiFoWriteInc & ~ReceiveFIFOWriteFull;
|
||||
// register map
|
||||
localparam SPI_SCKDIV = 8'h00;
|
||||
localparam SPI_SCKMODE = 8'h04;
|
||||
localparam SPI_CSID = 8'h10;
|
||||
localparam SPI_CSDEF = 8'h14;
|
||||
localparam SPI_CSMODE = 8'h18;
|
||||
localparam SPI_DELAY0 = 8'h28;
|
||||
localparam SPI_DELAY1 = 8'h2C;
|
||||
localparam SPI_FMT = 8'h40;
|
||||
localparam SPI_TXDATA = 8'h48;
|
||||
localparam SPI_RXDATA = 8'h4C;
|
||||
localparam SPI_TXMARK = 8'h50;
|
||||
localparam SPI_RXMARK = 8'h54;
|
||||
localparam SPI_IE = 8'h70;
|
||||
localparam SPI_IP = 8'h74;
|
||||
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) ReceiveFiFoWriteInc <= 1'b0;
|
||||
else if (SCLKenable & ReceiveShiftFull) ReceiveFiFoWriteInc <= 1'b1;
|
||||
else if (SCLKenable & ReceiveFiFoTakingData) ReceiveFiFoWriteInc <= 1'b0;
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) ReceiveShiftFullDelayPCLK <= 1'b0;
|
||||
else if (SCLKenableEarly) ReceiveShiftFullDelayPCLK <= ReceiveShiftFull;
|
||||
// SPI control registers. Refer to SiFive FU540-C000 manual
|
||||
logic [11:0] SckDiv;
|
||||
logic [1:0] SckMode;
|
||||
logic [1:0] ChipSelectID;
|
||||
logic [3:0] ChipSelectDef;
|
||||
logic [1:0] ChipSelectMode;
|
||||
logic [15:0] Delay0, Delay1;
|
||||
logic [4:0] Format;
|
||||
logic [7:0] ReceiveData;
|
||||
logic [8:0] TransmitData;
|
||||
logic [2:0] TransmitWatermark, ReceiveWatermark;
|
||||
logic [1:0] InterruptEnable, InterruptPending;
|
||||
|
||||
// Bus interface signals
|
||||
logic [7:0] Entry;
|
||||
logic Memwrite;
|
||||
logic [31:0] Din, Dout;
|
||||
|
||||
// SPI Controller signals
|
||||
logic SCLKenable;
|
||||
logic EndOfFrame;
|
||||
logic Transmitting;
|
||||
logic InactiveState;
|
||||
logic [3:0] FrameLength;
|
||||
|
||||
// Starting Transmission and restarting SCLKenable
|
||||
logic ResetSCLKenable;
|
||||
logic TransmitStart;
|
||||
logic TransmitStartD;
|
||||
|
||||
// Main FSM which controls SPI transmission
|
||||
typedef enum logic [2:0] {CS_INACTIVE, DELAY_0, ACTIVE_0, ACTIVE_1, DELAY_1,INTER_CS, INTER_XFR} statetype;
|
||||
statetype state;
|
||||
// Transmit Start State Machine Variables
|
||||
typedef enum logic [1:0] {READY, START, WAIT} txState;
|
||||
txState CurrState, NextState;
|
||||
|
||||
// FIFO Watermark signals - TransmitReadMark = ip[0], ReceiveWriteMark = ip[1]
|
||||
logic TransmitWriteMark, TransmitReadMark, ReceiveWriteMark, ReceiveReadMark;
|
||||
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) begin
|
||||
state <= CS_INACTIVE;
|
||||
FrameCount <= 4'b0;
|
||||
SPICLK <= SckMode[1];
|
||||
end else if (SCLKenable) begin
|
||||
/* verilator lint_off CASEINCOMPLETE */
|
||||
case (state)
|
||||
CS_INACTIVE: begin
|
||||
CS_SCKCount <= 9'b1;
|
||||
SCK_CSCount <= 9'b10;
|
||||
FrameCount <= 4'b0;
|
||||
InterCSCount <= 9'b10;
|
||||
InterXFRCount <= 9'b1;
|
||||
if ((~TransmitFIFOReadEmpty | ~TransmitShiftEmpty) & ((|(Delay0[7:0])) | ~SckMode[0])) state <= DELAY_0;
|
||||
else if ((~TransmitFIFOReadEmpty | ~TransmitShiftEmpty)) begin
|
||||
state <= ACTIVE_0;
|
||||
SPICLK <= ~SckMode[1];
|
||||
end else SPICLK <= SckMode[1];
|
||||
end
|
||||
DELAY_0: begin
|
||||
CS_SCKCount <= CS_SCKCount + 9'b1;
|
||||
if (CS_SCKCount >= (({Delay0[7:0], 1'b0}) + ImplicitDelay1)) begin
|
||||
state <= ACTIVE_0;
|
||||
SPICLK <= ~SckMode[1];
|
||||
end
|
||||
end
|
||||
ACTIVE_0: begin
|
||||
FrameCount <= FrameCount + 4'b1;
|
||||
SPICLK <= SckMode[1];
|
||||
state <= ACTIVE_1;
|
||||
end
|
||||
ACTIVE_1: begin
|
||||
InterXFRCount <= 9'b1;
|
||||
if (FrameCount < Format[4:1]) begin
|
||||
state <= ACTIVE_0;
|
||||
SPICLK <= ~SckMode[1];
|
||||
end
|
||||
else if ((ChipSelectMode[1:0] == 2'b10) & ~|(Delay1[15:8]) & (~TransmitFIFOReadEmpty)) begin
|
||||
state <= ACTIVE_0;
|
||||
SPICLK <= ~SckMode[1];
|
||||
CS_SCKCount <= 9'b1;
|
||||
SCK_CSCount <= 9'b10;
|
||||
FrameCount <= 4'b0;
|
||||
InterCSCount <= 9'b10;
|
||||
end
|
||||
else if (ChipSelectMode[1:0] == 2'b10) state <= INTER_XFR;
|
||||
else if (~|(Delay0[15:8]) & (~SckMode[0])) state <= INTER_CS;
|
||||
else state <= DELAY_1;
|
||||
end
|
||||
DELAY_1: begin
|
||||
SCK_CSCount <= SCK_CSCount + 9'b1;
|
||||
if (SCK_CSCount >= (({Delay0[15:8], 1'b0}) + ImplicitDelay2)) state <= INTER_CS;
|
||||
end
|
||||
INTER_CS: begin
|
||||
InterCSCount <= InterCSCount + 9'b1;
|
||||
SPICLK <= SckMode[1];
|
||||
if (InterCSCount >= ({Delay1[7:0],1'b0})) state <= CS_INACTIVE;
|
||||
end
|
||||
INTER_XFR: begin
|
||||
CS_SCKCount <= 9'b1;
|
||||
SCK_CSCount <= 9'b10;
|
||||
FrameCount <= 4'b0;
|
||||
InterCSCount <= 9'b10;
|
||||
InterXFRCount <= InterXFRCount + 9'b1;
|
||||
if ((InterXFRCount >= ({Delay1[15:8], 1'b0})) & (~TransmitFIFOReadEmptyDelay | ~TransmitShiftEmpty)) begin
|
||||
state <= ACTIVE_0;
|
||||
SPICLK <= ~SckMode[1];
|
||||
end else if (~|ChipSelectMode[1:0]) state <= CS_INACTIVE;
|
||||
else SPICLK <= SckMode[1];
|
||||
end
|
||||
endcase
|
||||
/* verilator lint_off CASEINCOMPLETE */
|
||||
end
|
||||
// Transmit FIFO Signals
|
||||
logic TransmitFIFOFull, TransmitFIFOEmpty;
|
||||
logic TransmitFIFOWriteInc;
|
||||
logic TransmitFIFOReadInc; // Increments Tx FIFO read ptr 1 cycle after Tx FIFO is read
|
||||
logic [7:0] TransmitReadData;
|
||||
|
||||
|
||||
// ReceiveFIFO Signals
|
||||
logic ReceiveFIFOWriteInc;
|
||||
logic ReceiveFIFOReadInc;
|
||||
logic ReceiveFIFOFull, ReceiveFIFOEmpty;
|
||||
|
||||
/* verilator lint_off UNDRIVEN */
|
||||
logic [2:0] TransmitWriteWatermarkLevel, ReceiveReadWatermarkLevel; // unused generic FIFO outputs
|
||||
/* verilator lint_off UNDRIVEN */
|
||||
logic [7:0] ReceiveShiftRegEndian; // Reverses ReceiveShiftReg if Format[2] set (little endian transmission)
|
||||
|
||||
assign DelayMode = SckMode[0] ? (state == DELAY_1) : (state == ACTIVE_1 & ReceiveShiftFull);
|
||||
assign ChipSelectInternal = (state == CS_INACTIVE | state == INTER_CS | DelayMode & ~|(Delay0[15:8])) ? ChipSelectDef : ~ChipSelectDef;
|
||||
assign Active = (state == ACTIVE_0 | state == ACTIVE_1);
|
||||
assign SampleEdge = SckMode[0] ? (state == ACTIVE_1) : (state == ACTIVE_0);
|
||||
assign ZeroDelayHoldMode = ((ChipSelectMode == 2'b10) & (~|(Delay1[7:4])));
|
||||
assign TransmitInactive = ((state == INTER_CS) | (state == CS_INACTIVE) | (state == INTER_XFR) | (ReceiveShiftFullDelayPCLK & ZeroDelayHoldMode) | ((state == ACTIVE_1) & ((ChipSelectMode[1:0] == 2'b10) & ~|(Delay1[15:8]) & (~TransmitFIFOReadEmpty) & (FrameCount == Format[4:1]))));
|
||||
assign Active0 = (state == ACTIVE_0);
|
||||
assign ShiftEdgeSPICLK = ZeroDiv ? ~SPICLK : SPICLK;
|
||||
// Shift reg signals
|
||||
logic ShiftEdge; // Determines which edge of sck to shift from TransmitReg
|
||||
logic SampleEdge; // Determines which edge of sck to sample from ReceiveShiftReg
|
||||
logic [7:0] TransmitReg; // Transmit shift register
|
||||
logic [7:0] ReceiveShiftReg; // Receive shift register
|
||||
logic [7:0] TransmitDataEndian; // Reverses TransmitData from txFIFO if littleendian, since TransmitReg always shifts MSB
|
||||
logic TransmitLoad; // Determines when to load TransmitReg
|
||||
logic TransmitRegLoaded;
|
||||
|
||||
// Signal tracks which edge of sck to shift data
|
||||
always_comb
|
||||
case(SckMode[1:0])
|
||||
2'b00: ShiftEdge = ShiftEdgeSPICLK & SCLKenable;
|
||||
2'b01: ShiftEdge = (~ShiftEdgeSPICLK & ~FirstFrame & (|(FrameCount) | (CS_SCKCount >= (({Delay0[7:0], 1'b0}) + ImplicitDelay1))) & SCLKenable & (FrameCount != Format[4:1]) & ~TransmitInactive);
|
||||
2'b10: ShiftEdge = ~ShiftEdgeSPICLK & SCLKenable;
|
||||
2'b11: ShiftEdge = (ShiftEdgeSPICLK & ~FirstFrame & (|(FrameCount) | (CS_SCKCount >= (({Delay0[7:0], 1'b0}) + ImplicitDelay1))) & SCLKenable & (FrameCount != Format[4:1]) & ~TransmitInactive);
|
||||
default: ShiftEdge = ShiftEdgeSPICLK & SCLKenable;
|
||||
// Shift stuff due to Format register?
|
||||
logic ShiftIn; // Determines whether to shift from SPIIn or SPIOut (if SPI_LOOPBACK_TEST)
|
||||
logic [3:0] LeftShiftAmount; // Determines left shift amount to left-align data when little endian
|
||||
logic [7:0] ASR; // AlignedReceiveShiftReg
|
||||
|
||||
// CS signals
|
||||
logic [3:0] ChipSelectAuto; // Assigns ChipSelect value to selected CS signal based on CS ID
|
||||
logic [3:0] ChipSelectInternal; // Defines what each ChipSelect signal should be based on transmission status and ChipSelectDef
|
||||
|
||||
// APB access
|
||||
assign Entry = {PADDR[7:2],2'b00}; // 32-bit word-aligned accesses
|
||||
assign Memwrite = PWRITE & PENABLE & PSEL; // Only write in access phase
|
||||
assign PREADY = 1'b1;
|
||||
|
||||
// Account for subword read/write circuitry
|
||||
// -- Note SPI registers are 32 bits no matter what; access them with LW SW.
|
||||
|
||||
assign Din = PWDATA[31:0];
|
||||
if (P.XLEN == 64) assign PRDATA = { Dout, Dout};
|
||||
else assign PRDATA = Dout;
|
||||
|
||||
// Register access
|
||||
always_ff@(posedge PCLK)
|
||||
if (~PRESETn) begin
|
||||
SckDiv <= 12'd3;
|
||||
SckMode <= 2'b0;
|
||||
ChipSelectID <= 2'b0;
|
||||
ChipSelectDef <= 4'b1111;
|
||||
ChipSelectMode <= 2'b0;
|
||||
Delay0 <= {8'b1,8'b1};
|
||||
Delay1 <= {8'b0,8'b1};
|
||||
Format <= {5'b10000};
|
||||
TransmitData <= 9'b0;
|
||||
TransmitWatermark <= 3'b0;
|
||||
ReceiveWatermark <= 3'b0;
|
||||
InterruptEnable <= 2'b0;
|
||||
InterruptPending <= 2'b0;
|
||||
end else begin // writes
|
||||
/* verilator lint_off CASEINCOMPLETE */
|
||||
if (Memwrite)
|
||||
case(Entry) // flop to sample inputs
|
||||
SPI_SCKDIV: SckDiv <= Din[11:0];
|
||||
SPI_SCKMODE: SckMode <= Din[1:0];
|
||||
SPI_CSID: ChipSelectID <= Din[1:0];
|
||||
SPI_CSDEF: ChipSelectDef <= Din[3:0];
|
||||
SPI_CSMODE: ChipSelectMode <= Din[1:0];
|
||||
SPI_DELAY0: Delay0 <= {Din[23:16], Din[7:0]};
|
||||
SPI_DELAY1: Delay1 <= {Din[23:16], Din[7:0]};
|
||||
SPI_FMT: Format <= {Din[19:16], Din[2]};
|
||||
SPI_TXMARK: TransmitWatermark <= Din[2:0];
|
||||
SPI_RXMARK: ReceiveWatermark <= Din[2:0];
|
||||
SPI_IE: InterruptEnable <= Din[1:0];
|
||||
endcase
|
||||
|
||||
// Transmit shift register
|
||||
assign TransmitDataEndian = Format[0] ? {TransmitFIFOReadData[0], TransmitFIFOReadData[1], TransmitFIFOReadData[2], TransmitFIFOReadData[3], TransmitFIFOReadData[4], TransmitFIFOReadData[5], TransmitFIFOReadData[6], TransmitFIFOReadData[7]} : TransmitFIFOReadData[7:0];
|
||||
always_ff @(posedge PCLK)
|
||||
if(~PRESETn) TransmitShiftReg <= 8'b0;
|
||||
else if (TransmitShiftRegLoadSingleCycle) TransmitShiftReg <= TransmitDataEndian;
|
||||
else if (ShiftEdge & Active) TransmitShiftReg <= {TransmitShiftReg[6:0], TransmitShiftReg[0]};
|
||||
|
||||
assign SPIOut = TransmitShiftReg[7];
|
||||
|
||||
// If in loopback mode, receive shift register is connected directly to module's output pins. Else, connected to SPIIn
|
||||
// There are no setup/hold time issues because transmit shift register and receive shift register always shift/sample on opposite edges
|
||||
assign ShiftIn = P.SPI_LOOPBACK_TEST ? SPIOut : SPIIn;
|
||||
|
||||
// Receive shift register
|
||||
always_ff @(posedge PCLK)
|
||||
if(~PRESETn) ReceiveShiftReg <= 8'b0;
|
||||
else if (SampleEdge & SCLKenable) begin
|
||||
if (~Active) ReceiveShiftReg <= 8'b0;
|
||||
else ReceiveShiftReg <= {ReceiveShiftReg[6:0], ShiftIn};
|
||||
end
|
||||
|
||||
// Aligns received data and reverses if little-endian
|
||||
assign LeftShiftAmount = 4'h8 - Format[4:1];
|
||||
assign ASR = ReceiveShiftReg << LeftShiftAmount[2:0];
|
||||
assign ReceiveShiftRegEndian = Format[0] ? {ASR[0], ASR[1], ASR[2], ASR[3], ASR[4], ASR[5], ASR[6], ASR[7]} : ASR[7:0];
|
||||
|
||||
// Interrupt logic: raise interrupt if any enabled interrupts are pending
|
||||
assign SPIIntr = |(InterruptPending & InterruptEnable);
|
||||
|
||||
// Chip select logic
|
||||
always_comb
|
||||
case(ChipSelectID[1:0])
|
||||
2'b00: ChipSelectAuto = {ChipSelectDef[3], ChipSelectDef[2], ChipSelectDef[1], ChipSelectInternal[0]};
|
||||
2'b01: ChipSelectAuto = {ChipSelectDef[3],ChipSelectDef[2], ChipSelectInternal[1], ChipSelectDef[0]};
|
||||
2'b10: ChipSelectAuto = {ChipSelectDef[3],ChipSelectInternal[2], ChipSelectDef[1], ChipSelectDef[0]};
|
||||
2'b11: ChipSelectAuto = {ChipSelectInternal[3],ChipSelectDef[2], ChipSelectDef[1], ChipSelectDef[0]};
|
||||
if (Memwrite)
|
||||
case(Entry)
|
||||
SPI_TXDATA: if (~TransmitFIFOFull) TransmitData[7:0] <= Din[7:0];
|
||||
endcase
|
||||
/* verilator lint_off CASEINCOMPLETE */
|
||||
|
||||
// According to FU540 spec: Once interrupt is pending, it will remain set until number
|
||||
// of entries in tx/rx fifo is strictly more/less than tx/rxmark
|
||||
InterruptPending[0] <= TransmitReadMark;
|
||||
InterruptPending[1] <= ReceiveWriteMark;
|
||||
|
||||
case(Entry) // Flop to sample inputs
|
||||
SPI_SCKDIV: Dout <= {20'b0, SckDiv};
|
||||
SPI_SCKMODE: Dout <= {30'b0, SckMode};
|
||||
SPI_CSID: Dout <= {30'b0, ChipSelectID};
|
||||
SPI_CSDEF: Dout <= {28'b0, ChipSelectDef};
|
||||
SPI_CSMODE: Dout <= {30'b0, ChipSelectMode};
|
||||
SPI_DELAY0: Dout <= {8'b0, Delay0[15:8], 8'b0, Delay0[7:0]};
|
||||
SPI_DELAY1: Dout <= {8'b0, Delay1[15:8], 8'b0, Delay1[7:0]};
|
||||
SPI_FMT: Dout <= {12'b0, Format[4:1], 13'b0, Format[0], 2'b0};
|
||||
SPI_TXDATA: Dout <= {TransmitFIFOFull, 23'b0, 8'b0};
|
||||
SPI_RXDATA: Dout <= {ReceiveFIFOEmpty, 23'b0, ReceiveData[7:0]};
|
||||
SPI_TXMARK: Dout <= {29'b0, TransmitWatermark};
|
||||
SPI_RXMARK: Dout <= {29'b0, ReceiveWatermark};
|
||||
SPI_IE: Dout <= {30'b0, InterruptEnable};
|
||||
SPI_IP: Dout <= {30'b0, InterruptPending};
|
||||
default: Dout <= 32'b0;
|
||||
endcase
|
||||
end
|
||||
|
||||
assign SPICS = ChipSelectMode[0] ? ChipSelectDef : ChipSelectAuto;
|
||||
// SPI Controller module -------------------------------------------
|
||||
// This module controls state and timing signals that drive the rest of this module
|
||||
assign ResetSCLKenable = Memwrite & (Entry == SPI_SCKDIV);
|
||||
assign FrameLength = Format[4:1];
|
||||
|
||||
spi_controller controller(PCLK, PRESETn,
|
||||
// Transmit Signals
|
||||
TransmitStart, TransmitRegLoaded, ResetSCLKenable,
|
||||
// Register Inputs
|
||||
SckDiv, SckMode, ChipSelectMode, Delay0, Delay1, FrameLength,
|
||||
// txFIFO stuff
|
||||
TransmitFIFOEmpty,
|
||||
// Timing
|
||||
SCLKenable, ShiftEdge, SampleEdge, EndOfFrame,
|
||||
// State stuff
|
||||
Transmitting, InactiveState,
|
||||
// Outputs
|
||||
SPICLK);
|
||||
|
||||
// Transmit FIFO ---------------------------------------------------
|
||||
|
||||
// txFIFO write increment logic
|
||||
flopr #(1) txwincreg(PCLK, ~PRESETn,
|
||||
(Memwrite & (Entry == SPI_TXDATA) & ~TransmitFIFOFull),
|
||||
TransmitFIFOWriteInc);
|
||||
|
||||
// txFIFO read increment logic
|
||||
flopenr #(1) txrincreg(PCLK, ~PRESETn, SCLKenable,
|
||||
TransmitStartD | (EndOfFrame & ~TransmitFIFOEmpty),
|
||||
TransmitFIFOReadInc);
|
||||
|
||||
// Check whether TransmitReg has been loaded.
|
||||
// We use this signal to prevent returning to the Ready state for TransmitStart
|
||||
always_ff @(posedge PCLK) begin
|
||||
if (~PRESETn) begin
|
||||
TransmitRegLoaded <= 1'b0;
|
||||
end else if (TransmitLoad) begin
|
||||
TransmitRegLoaded <= 1'b1;
|
||||
end else if (ShiftEdge | EndOfFrame) begin
|
||||
TransmitRegLoaded <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// Setup TransmitStart state machine
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) CurrState <= READY;
|
||||
else CurrState <= NextState;
|
||||
|
||||
// State machine for starting transmissions
|
||||
always_comb begin
|
||||
case (CurrState)
|
||||
READY: if (~TransmitFIFOEmpty & ~Transmitting) NextState = START;
|
||||
else NextState = READY;
|
||||
START: NextState = WAIT;
|
||||
WAIT: if (~Transmitting & ~TransmitRegLoaded) NextState = READY;
|
||||
else NextState = WAIT;
|
||||
default: NextState = READY;
|
||||
endcase
|
||||
end
|
||||
|
||||
// Delayed TransmitStart signal for incrementing tx read point.
|
||||
assign TransmitStart = (CurrState == START);
|
||||
always_ff @(posedge PCLK)
|
||||
if (~PRESETn) TransmitStartD <= 1'b0;
|
||||
else if (TransmitStart) TransmitStartD <= 1'b1;
|
||||
else if (SCLKenable) TransmitStartD <= 1'b0;
|
||||
|
||||
// Transmit FIFO
|
||||
spi_fifo #(3,8) txFIFO(PCLK, 1'b1, SCLKenable, PRESETn,
|
||||
TransmitFIFOWriteInc, TransmitFIFOReadInc,
|
||||
TransmitData[7:0],
|
||||
TransmitWriteWatermarkLevel, TransmitWatermark[2:0],
|
||||
TransmitReadData[7:0],
|
||||
TransmitFIFOFull,
|
||||
TransmitFIFOEmpty,
|
||||
TransmitWriteMark, TransmitReadMark);
|
||||
|
||||
// Receive FIFO ----------------------------------------------------
|
||||
|
||||
// Receive FIFO Read Increment register
|
||||
flopr #(1) rxfiforincreg(PCLK, ~PRESETn,
|
||||
((Entry == SPI_RXDATA) & ~ReceiveFIFOEmpty & PSEL & ~ReceiveFIFOReadInc),
|
||||
ReceiveFIFOReadInc);
|
||||
|
||||
// Receive FIFO Write Increment register
|
||||
flopenr #(1) rxfifowincreg(PCLK, ~PRESETn, SCLKenable,
|
||||
EndOfFrame, ReceiveFIFOWriteInc);
|
||||
|
||||
// Receive FIFO
|
||||
spi_fifo #(3,8) rxFIFO(PCLK, SCLKenable, 1'b1, PRESETn,
|
||||
ReceiveFIFOWriteInc, ReceiveFIFOReadInc,
|
||||
ReceiveShiftRegEndian, ReceiveWatermark[2:0],
|
||||
ReceiveReadWatermarkLevel,
|
||||
ReceiveData[7:0],
|
||||
ReceiveFIFOFull,
|
||||
ReceiveFIFOEmpty,
|
||||
ReceiveWriteMark, ReceiveReadMark);
|
||||
|
||||
// Shift Registers --------------------------------------------------
|
||||
// Transmit shift register
|
||||
assign TransmitLoad = TransmitStart | (EndOfFrame & ~TransmitFIFOEmpty);
|
||||
assign TransmitDataEndian = Format[0] ? {<<{TransmitReadData[7:0]}} : TransmitReadData[7:0];
|
||||
always_ff @(posedge PCLK)
|
||||
if(~PRESETn) TransmitReg <= 8'b0;
|
||||
else if (TransmitLoad) TransmitReg <= TransmitDataEndian;
|
||||
else if (ShiftEdge) TransmitReg <= {TransmitReg[6:0], TransmitReg[0]};
|
||||
|
||||
assign SPIOut = TransmitReg[7];
|
||||
|
||||
// If in loopback mode, receive shift register is connected directly
|
||||
// to module's output pins. Else, connected to SPIIn. There are no
|
||||
// setup/hold time issues because transmit shift register and receive
|
||||
// shift register always shift/sample on opposite edges
|
||||
assign ShiftIn = P.SPI_LOOPBACK_TEST ? SPIOut : SPIIn;
|
||||
|
||||
// Receive shift register
|
||||
always_ff @(posedge PCLK)
|
||||
if(~PRESETn) begin
|
||||
ReceiveShiftReg <= 8'b0;
|
||||
end else if (SampleEdge) begin
|
||||
ReceiveShiftReg <= {ReceiveShiftReg[6:0], ShiftIn};
|
||||
end
|
||||
|
||||
// Aligns received data and reverses if little-endian
|
||||
assign LeftShiftAmount = 4'h8 - FrameLength;
|
||||
assign ASR = ReceiveShiftReg << LeftShiftAmount[2:0];
|
||||
assign ReceiveShiftRegEndian = Format[0] ? {<<{ASR[7:0]}} : ASR[7:0];
|
||||
|
||||
// Interrupt logic: raise interrupt if any enabled interrupts are pending
|
||||
assign SPIIntr = |(InterruptPending & InterruptEnable);
|
||||
|
||||
// Chip select logic
|
||||
assign ChipSelectInternal = InactiveState ? ChipSelectDef : ~ChipSelectDef;
|
||||
always_comb
|
||||
case(ChipSelectID[1:0])
|
||||
2'b00: ChipSelectAuto = {ChipSelectDef[3], ChipSelectDef[2], ChipSelectDef[1], ChipSelectInternal[0]};
|
||||
2'b01: ChipSelectAuto = {ChipSelectDef[3],ChipSelectDef[2], ChipSelectInternal[1], ChipSelectDef[0]};
|
||||
2'b10: ChipSelectAuto = {ChipSelectDef[3],ChipSelectInternal[2], ChipSelectDef[1], ChipSelectDef[0]};
|
||||
2'b11: ChipSelectAuto = {ChipSelectInternal[3],ChipSelectDef[2], ChipSelectDef[1], ChipSelectDef[0]};
|
||||
endcase
|
||||
assign SPICS = ChipSelectMode[0] ? ChipSelectDef : ChipSelectAuto;
|
||||
|
||||
endmodule
|
||||
|
@ -1,9 +1,8 @@
|
||||
///////////////////////////////////////////
|
||||
// spi_controller.sv
|
||||
//
|
||||
// Written: jacobpease@protonmail.com
|
||||
// Written: Jacob Pease jacobpease@protonmail.com
|
||||
// Created: October 28th, 2024
|
||||
// Modified:
|
||||
//
|
||||
// Purpose: Controller logic for SPI
|
||||
//
|
||||
@ -12,7 +11,7 @@
|
||||
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||
// https://github.com/openhwgroup/cvw
|
||||
//
|
||||
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
|
||||
// Copyright (C) 2021-24 Harvey Mudd College & Oklahoma State University
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||
//
|
||||
@ -28,21 +27,34 @@
|
||||
// and limitations under the License.
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
module spi_controller (
|
||||
module spi_controller (
|
||||
input logic PCLK,
|
||||
input logic PRESETn,
|
||||
|
||||
// Start Transmission
|
||||
input logic TransmitStart,
|
||||
input logic TransmitRegLoaded,
|
||||
input logic ResetSCLKenable,
|
||||
|
||||
// Registers
|
||||
input logic [11:0] SckDiv,
|
||||
input logic [1:0] SckMode,
|
||||
input logic [1:0] CSMode,
|
||||
input logic [15:0] Delay0,
|
||||
input logic [15:0] Delay1,
|
||||
input logic [7:0] txFIFORead,
|
||||
input logic txFIFOReadEmpty,
|
||||
output logic SPICLK,
|
||||
output logic SPIOUT,
|
||||
output logic CS
|
||||
input logic [15:0] Delay0,
|
||||
input logic [15:0] Delay1,
|
||||
input logic [3:0] FrameLength,
|
||||
|
||||
// Is the Transmit FIFO Empty?
|
||||
input logic TransmitFIFOEmpty,
|
||||
|
||||
// Control signals
|
||||
output logic SCLKenable,
|
||||
output logic ShiftEdge,
|
||||
output logic SampleEdge,
|
||||
output logic EndOfFrame,
|
||||
output logic Transmitting,
|
||||
output logic InactiveState,
|
||||
output logic SPICLK
|
||||
);
|
||||
|
||||
// CSMode Stuff
|
||||
@ -50,44 +62,31 @@ module spi_controller (
|
||||
localparam AUTOMODE = 2'b00;
|
||||
localparam OFFMODE = 2'b11;
|
||||
|
||||
// FSM States
|
||||
typedef enum logic [2:0] {INACTIVE, CSSCK, TRANSMIT, SCKCS, HOLD, INTERCS, INTERXFR} statetype;
|
||||
statetype CurrState, NextState;
|
||||
|
||||
// SCLKenable stuff
|
||||
logic [11:0] DivCounter;
|
||||
logic SCLKenable;
|
||||
logic SCLKenableEarly;
|
||||
logic SCLKenableLate;
|
||||
logic EdgeTiming;
|
||||
logic ZeroDiv;
|
||||
logic Clock0;
|
||||
logic Clock1;
|
||||
logic SCK; // SUPER IMPORTANT, THIS CAN'T BE THE SAME AS SPICLK!
|
||||
|
||||
logic SCK;
|
||||
|
||||
// Shift and Sample Edges
|
||||
logic PreShiftEdge;
|
||||
logic PreSampleEdge;
|
||||
logic ShiftEdge;
|
||||
logic SampleEdge;
|
||||
logic EdgePulse;
|
||||
logic ShiftEdgePulse;
|
||||
logic SampleEdgePulse;
|
||||
logic EndOfFramePulse;
|
||||
logic PhaseOneOffset;
|
||||
|
||||
// Frame stuff
|
||||
logic [2:0] BitNum;
|
||||
logic [3:0] BitNum;
|
||||
logic LastBit;
|
||||
logic EndOfFrame;
|
||||
logic EndOfFrameDelay;
|
||||
logic PhaseOneOffset;
|
||||
|
||||
// Transmit Stuff
|
||||
logic ContinueTransmit;
|
||||
|
||||
// SPIOUT Stuff
|
||||
logic TransmitLoad;
|
||||
logic [7:0] TransmitReg;
|
||||
logic Transmitting;
|
||||
logic EndTransmission;
|
||||
|
||||
logic HoldMode;
|
||||
// logic TransmitRegLoaded; // TODO: Could be replaced by TransmitRegLoaded?
|
||||
logic NextEndDelay;
|
||||
logic CurrentEndDelay;
|
||||
|
||||
// Delay Stuff
|
||||
logic [7:0] cssck;
|
||||
@ -104,13 +103,12 @@ module spi_controller (
|
||||
logic EndOfSCKCS;
|
||||
logic EndOfINTERCS;
|
||||
logic EndOfINTERXFR;
|
||||
logic EndOfDelay;
|
||||
|
||||
logic [7:0] CSSCKCounter;
|
||||
logic [7:0] SCKCSCounter;
|
||||
logic [7:0] INTERCSCounter;
|
||||
logic [7:0] INTERXFRCounter;
|
||||
logic [7:0] DelayCounter;
|
||||
|
||||
logic DelayIsNext;
|
||||
logic DelayState;
|
||||
|
||||
// Convenient Delay Reg Names
|
||||
assign cssck = Delay0[7:0];
|
||||
@ -125,67 +123,46 @@ module spi_controller (
|
||||
assign HasINTERXFR = interxfr > 8'b0;
|
||||
|
||||
// Have we hit full delay for any of the delays?
|
||||
assign EndOfCSSCK = CSSCKCounter == cssck;
|
||||
assign EndOfSCKCS = SCKCSCounter == sckcs;
|
||||
assign EndOfINTERCS = INTERCSCounter == intercs;
|
||||
assign EndOfINTERXFR = INTERXFRCounter == interxfr;
|
||||
assign EndOfCSSCK = (DelayCounter == cssck) & (CurrState == CSSCK);
|
||||
assign EndOfSCKCS = (DelayCounter == sckcs) & (CurrState == SCKCS);
|
||||
assign EndOfINTERCS = (DelayCounter == intercs) & (CurrState == INTERCS);
|
||||
assign EndOfINTERXFR = (DelayCounter == interxfr) & (CurrState == INTERXFR);
|
||||
|
||||
assign EndOfDelay = EndOfCSSCK | EndOfSCKCS | EndOfINTERCS | EndOfINTERXFR;
|
||||
|
||||
// Clock Signal Stuff -----------------------------------------------
|
||||
// I'm going to handle all clock stuff here, including ShiftEdge and
|
||||
// SampleEdge. This makes sure that SPICLK is an output of a register
|
||||
// and it properly synchronizes signals.
|
||||
|
||||
assign SCLKenableLate = DivCounter > SckDiv;
|
||||
|
||||
// SPI enable generation, where SCLK = PCLK/(2*(SckDiv + 1))
|
||||
// Asserts SCLKenable at the rising and falling edge of SCLK by counting from 0 to SckDiv
|
||||
// Active at 2x SCLK frequency to account for implicit half cycle delays and actions on both clock edges depending on phase
|
||||
assign SCLKenable = DivCounter == SckDiv;
|
||||
assign SCLKenableEarly = (DivCounter + 1'b1) == SckDiv;
|
||||
assign LastBit = BitNum == 3'd7;
|
||||
assign EdgeTiming = SckDiv > 12'b0 ? SCLKenableEarly : SCLKenable;
|
||||
|
||||
//assign SPICLK = Clock0;
|
||||
|
||||
assign ContinueTransmit = ~txFIFOReadEmpty & EndOfFrame;
|
||||
assign EndTransmission = txFIFOReadEmpty & EndOfFrameDelay;
|
||||
assign ContinueTransmit = ~TransmitFIFOEmpty & EndOfFrame;
|
||||
assign EndTransmission = TransmitFIFOEmpty & EndOfFrame;
|
||||
|
||||
always_ff @(posedge PCLK) begin
|
||||
if (~PRESETn) begin
|
||||
DivCounter <= 12'b0;
|
||||
SPICLK <= SckMode[1];
|
||||
SCK <= 0;
|
||||
BitNum <= 3'h0;
|
||||
PreShiftEdge <= 0;
|
||||
PreSampleEdge <= 0;
|
||||
EndOfFrame <= 0;
|
||||
BitNum <= 4'h0;
|
||||
DelayCounter <= 0;
|
||||
end else begin
|
||||
// TODO: Consolidate into one delay counter since none of the
|
||||
// delays happen at the same time?
|
||||
// SCK logic for delay times
|
||||
if (TransmitStart) begin
|
||||
SCK <= 0;
|
||||
end else if (SCLKenable) begin
|
||||
SCK <= ~SCK;
|
||||
end
|
||||
|
||||
if ((CurrState == CSSCK) & SCK) begin
|
||||
CSSCKCounter <= CSSCKCounter + 8'd1;
|
||||
end else begin
|
||||
CSSCKCounter <= 8'd0;
|
||||
end
|
||||
|
||||
if ((CurrState == SCKCS) & SCK) begin
|
||||
SCKCSCounter <= SCKCSCounter + 8'd1;
|
||||
end else begin
|
||||
SCKCSCounter <= 8'd0;
|
||||
end
|
||||
|
||||
if ((CurrState == INTERCS) & SCK) begin
|
||||
INTERCSCounter <= INTERCSCounter + 8'd1;
|
||||
end else begin
|
||||
INTERCSCounter <= 8'd0;
|
||||
end
|
||||
|
||||
if ((CurrState == INTERXFR) & SCK) begin
|
||||
INTERXFRCounter <= INTERXFRCounter + 8'd1;
|
||||
end else begin
|
||||
INTERXFRCounter <= 8'd0;
|
||||
|
||||
// Counter for all four delay types
|
||||
if (DelayState & SCK & SCLKenable) begin
|
||||
DelayCounter <= DelayCounter + 8'd1;
|
||||
end else if (SCLKenable & EndOfDelay) begin
|
||||
DelayCounter <= 8'd0;
|
||||
end
|
||||
|
||||
// SPICLK Logic
|
||||
@ -196,32 +173,35 @@ module spi_controller (
|
||||
end
|
||||
|
||||
// Reset divider
|
||||
if (SCLKenable | TransmitStart) begin
|
||||
if (SCLKenable | TransmitStart | ResetSCLKenable) begin
|
||||
DivCounter <= 12'b0;
|
||||
end else begin
|
||||
DivCounter = DivCounter + 12'd1;
|
||||
end
|
||||
|
||||
// EndOfFrame controller
|
||||
// if (SckDiv > 0 ? SCLKenableEarly & LastBit & SPICLK : LastBit & ~SPICLK) begin
|
||||
// EndOfFrame <= 1'b1;
|
||||
// end else begin
|
||||
// EndOfFrame <= 1'b0;
|
||||
// end
|
||||
|
||||
if (~TransmitStart) begin
|
||||
EndOfFrame <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting;
|
||||
DivCounter <= DivCounter + 12'd1;
|
||||
end
|
||||
|
||||
// Increment BitNum
|
||||
if (ShiftEdge & Transmitting) begin
|
||||
BitNum <= BitNum + 3'd1;
|
||||
end else if (EndOfFrameDelay) begin
|
||||
BitNum <= 3'b0;
|
||||
BitNum <= BitNum + 4'd1;
|
||||
end else if (EndOfFrame) begin
|
||||
BitNum <= 4'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// The very last bit in a frame of any length.
|
||||
assign LastBit = (BitNum == FrameLength - 4'b1);
|
||||
|
||||
// Any SCLKenable pulse aligns with leading or trailing edge during
|
||||
// Transmission. We can use this signal as the basis for ShiftEdge
|
||||
// and SampleEdge.
|
||||
assign EdgePulse = SCLKenable & Transmitting;
|
||||
|
||||
// Possible pulses for all edge types. Combined with SPICLK to get
|
||||
// edges for different phase and polarity modes.
|
||||
assign ShiftEdgePulse = EdgePulse & ~LastBit;
|
||||
assign SampleEdgePulse = EdgePulse & ~DelayIsNext;
|
||||
assign EndOfFramePulse = EdgePulse & LastBit;
|
||||
|
||||
// Delay ShiftEdge and SampleEdge by a half PCLK period
|
||||
// Aligned EXACTLY ON THE MIDDLE of the leading and trailing edges.
|
||||
// Sweeeeeeeeeet...
|
||||
@ -230,21 +210,38 @@ module spi_controller (
|
||||
ShiftEdge <= 0;
|
||||
PhaseOneOffset <= 0;
|
||||
SampleEdge <= 0;
|
||||
EndOfFrameDelay <= 0;
|
||||
end else begin
|
||||
ShiftEdge <= ((SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & ~LastBit & Transmitting) & PhaseOneOffset;
|
||||
PhaseOneOffset <= PhaseOneOffset == 0 ? Transmitting & SCLKenable : PhaseOneOffset;
|
||||
SampleEdge <= (SckMode[1] ^ SckMode[0] ^ ~SPICLK) & SCLKenable & Transmitting;
|
||||
EndOfFrameDelay <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting;
|
||||
EndOfFrame <= 0;
|
||||
end else begin
|
||||
PhaseOneOffset <= (PhaseOneOffset == 0) ? Transmitting & SCLKenable : ~EndOfFrame;
|
||||
case(SckMode)
|
||||
2'b00: begin
|
||||
ShiftEdge <= SPICLK & ShiftEdgePulse;
|
||||
SampleEdge <= ~SPICLK & SampleEdgePulse;
|
||||
EndOfFrame <= SPICLK & EndOfFramePulse;
|
||||
end
|
||||
2'b01: begin
|
||||
ShiftEdge <= ~SPICLK & ShiftEdgePulse & PhaseOneOffset;
|
||||
SampleEdge <= SPICLK & SampleEdgePulse;
|
||||
EndOfFrame <= ~SPICLK & EndOfFramePulse;
|
||||
end
|
||||
2'b10: begin
|
||||
ShiftEdge <= ~SPICLK & ShiftEdgePulse;
|
||||
SampleEdge <= SPICLK & SampleEdgePulse;
|
||||
EndOfFrame <= ~SPICLK & EndOfFramePulse;
|
||||
end
|
||||
2'b11: begin
|
||||
ShiftEdge <= SPICLK & ShiftEdgePulse & PhaseOneOffset;
|
||||
SampleEdge <= ~SPICLK & SampleEdgePulse;
|
||||
EndOfFrame <= SPICLK & EndOfFramePulse;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
// typedef enum logic [2:0] {INACTIVE, CSSCK, TRANSMIT, SCKCS, HOLD, INTERCS, INTERXFR} statetype;
|
||||
// statetype CurrState, NextState;
|
||||
|
||||
assign HoldMode = CSMode == 2'b10;
|
||||
assign TransmitLoad = TransmitStart | (EndOfFrameDelay & ~txFIFOReadEmpty);
|
||||
|
||||
// Logic for continuing to transmit through Delay states after end of frame
|
||||
assign NextEndDelay = NextState == SCKCS | NextState == INTERCS | NextState == INTERXFR;
|
||||
assign CurrentEndDelay = CurrState == SCKCS | CurrState == INTERCS | CurrState == INTERXFR;
|
||||
|
||||
always_ff @(posedge PCLK) begin
|
||||
if (~PRESETn) begin
|
||||
CurrState <= INACTIVE;
|
||||
@ -255,71 +252,68 @@ module spi_controller (
|
||||
|
||||
always_comb begin
|
||||
case (CurrState)
|
||||
INACTIVE: begin // INACTIVE case --------------------------------
|
||||
if (TransmitStart) begin
|
||||
if (~HasCSSCK) begin
|
||||
NextState = TRANSMIT;
|
||||
end else begin
|
||||
NextState = CSSCK;
|
||||
end
|
||||
end else begin
|
||||
NextState = INACTIVE;
|
||||
end
|
||||
end
|
||||
CSSCK: begin // DELAY0 case -------------------------------------
|
||||
if (EndOfCSSCK) begin
|
||||
NextState = TRANSMIT;
|
||||
end
|
||||
end
|
||||
INACTIVE: if (TransmitRegLoaded) begin
|
||||
if (~HasCSSCK) NextState = TRANSMIT;
|
||||
else NextState = CSSCK;
|
||||
end else begin
|
||||
NextState = INACTIVE;
|
||||
end
|
||||
CSSCK: if (EndOfCSSCK) NextState = TRANSMIT;
|
||||
else NextState = CSSCK;
|
||||
TRANSMIT: begin // TRANSMIT case --------------------------------
|
||||
case(CSMode)
|
||||
AUTOMODE: begin
|
||||
if (EndTransmission) begin
|
||||
NextState = INACTIVE;
|
||||
end else if (ContinueTransmit) begin
|
||||
NextState = SCKCS;
|
||||
end
|
||||
if (EndTransmission) NextState = INACTIVE;
|
||||
else if (EndOfFrame) NextState = SCKCS;
|
||||
else NextState = TRANSMIT;
|
||||
end
|
||||
HOLDMODE: begin
|
||||
if (EndTransmission) begin
|
||||
NextState = HOLD;
|
||||
end else if (ContinueTransmit) begin
|
||||
if (HasINTERXFR) NextState = INTERXFR;
|
||||
end
|
||||
if (EndTransmission) NextState = HOLD;
|
||||
else if (ContinueTransmit & HasINTERXFR) NextState = INTERXFR;
|
||||
else NextState = TRANSMIT;
|
||||
end
|
||||
OFFMODE: begin
|
||||
|
||||
if (EndTransmission) NextState = INACTIVE;
|
||||
else if (ContinueTransmit & HasINTERXFR) NextState = INTERXFR;
|
||||
else NextState = TRANSMIT;
|
||||
end
|
||||
|
||||
default: NextState = TRANSMIT;
|
||||
endcase
|
||||
end
|
||||
SCKCS: begin // SCKCS case --------------------------------------
|
||||
if (EndOfSCKCS) begin
|
||||
if (EndTransmission) begin
|
||||
if (CSMode == AUTOMODE) NextState = INACTIVE;
|
||||
else if (CSMode == HOLDMODE) NextState = HOLD;
|
||||
end else if (ContinueTransmit) begin
|
||||
if (~TransmitRegLoaded) begin
|
||||
// if (CSMode == AUTOMODE) NextState = INACTIVE;
|
||||
if (CSMode == HOLDMODE) NextState = HOLD;
|
||||
else NextState = INACTIVE;
|
||||
end else begin
|
||||
if (HasINTERCS) NextState = INTERCS;
|
||||
else NextState = TRANSMIT;
|
||||
end
|
||||
end else begin
|
||||
NextState = SCKCS;
|
||||
end
|
||||
end
|
||||
HOLD: begin // HOLD mode case -----------------------------------
|
||||
if (CSMode == AUTOMODE) begin
|
||||
NextState = INACTIVE;
|
||||
end else if (TransmitStart) begin // If FIFO is written to, start again.
|
||||
end else if (TransmitRegLoaded) begin // If FIFO is written to, start again.
|
||||
NextState = TRANSMIT;
|
||||
end
|
||||
end else NextState = HOLD;
|
||||
end
|
||||
INTERCS: begin // INTERCS case ----------------------------------
|
||||
if (EndOfINTERCS) begin
|
||||
if (HasCSSCK) NextState = CSSCK;
|
||||
else NextState = TRANSMIT;
|
||||
end else begin
|
||||
NextState = INTERCS;
|
||||
end
|
||||
end
|
||||
INTERXFR: begin // INTERXFR case --------------------------------
|
||||
if (EndOfINTERXFR) begin
|
||||
NextState = TRANSMIT;
|
||||
end else begin
|
||||
NextState = INTERXFR;
|
||||
end
|
||||
end
|
||||
default: begin
|
||||
@ -330,19 +324,7 @@ module spi_controller (
|
||||
|
||||
assign Transmitting = CurrState == TRANSMIT;
|
||||
assign DelayIsNext = (NextState == CSSCK | NextState == SCKCS | NextState == INTERCS | NextState == INTERXFR);
|
||||
|
||||
//
|
||||
always_ff @(posedge PCLK) begin
|
||||
if (~PRESETn) begin
|
||||
TransmitReg <= 8'b0;
|
||||
end else if (TransmitLoad) begin
|
||||
TransmitReg <= txFIFORead;
|
||||
end else if (ShiftEdge) begin
|
||||
TransmitReg <= {TransmitReg[6:0], TransmitReg[0]};
|
||||
end
|
||||
end
|
||||
|
||||
assign SPIOUT = TransmitReg[7];
|
||||
assign CS = CurrState == INACTIVE | CurrState == INTERCS;
|
||||
assign DelayState = (CurrState == CSSCK | CurrState == SCKCS | CurrState == INTERCS | CurrState == INTERXFR);
|
||||
assign InactiveState = CurrState == INACTIVE | CurrState == INTERCS;
|
||||
|
||||
endmodule
|
||||
|
@ -19,6 +19,10 @@ module spi_fifo #(parameter M=3, N=8)( // 2^M entries of N bits
|
||||
logic [M:0] rptrnext, wptrnext;
|
||||
logic [M-1:0] raddr;
|
||||
logic [M-1:0] waddr;
|
||||
|
||||
logic [M-1:0] numVals;
|
||||
|
||||
assign numVals = waddr - raddr;
|
||||
|
||||
assign rdata = mem[raddr];
|
||||
always_ff @(posedge PCLK)
|
||||
|
@ -72,7 +72,8 @@ string coverage64gc[] = '{
|
||||
"pmpcfg2",
|
||||
"pmppriority",
|
||||
"pmpcbo",
|
||||
"pmpadrdecs"
|
||||
"pmpadrdecs",
|
||||
"btbthrash"
|
||||
};
|
||||
|
||||
string buildroot[] = '{
|
||||
|
145
tests/coverage/btbthrash.S
Normal file
145
tests/coverage/btbthrash.S
Normal file
@ -0,0 +1,145 @@
|
||||
///////////////////////////////////////////
|
||||
// btbtrash.S
|
||||
//
|
||||
// Written: Rose Thompson rose@rosethompson.net 23 October 2024
|
||||
//
|
||||
// Purpose: Test the branch target buffer alias with divide and cache pipeline stalls
|
||||
//
|
||||
// A component of the CORE-V-WALLY configurable RISC-V project.
|
||||
// https://github.com/openhwgroup/cvw
|
||||
//
|
||||
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
|
||||
//
|
||||
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
|
||||
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
|
||||
// may obtain a copy of the License at
|
||||
//
|
||||
// https://solderpad.org/licenses/SHL-2.1/
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, any work distributed under the
|
||||
// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
// either express or implied. See the License for the specific language governing permissions
|
||||
// and limitations under the License.
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// load code to initalize stack, handle interrupts, terminate
|
||||
#include "WALLY-init-lib.h"
|
||||
|
||||
main:
|
||||
|
||||
# Division test (having trouble with buildroot)
|
||||
li x1, 1938759018
|
||||
li x2, 3745029
|
||||
li x3, 458
|
||||
li x4, 29587209347
|
||||
li x5, 28957
|
||||
li x6, 298
|
||||
li x7, 238562
|
||||
li x8, 198674
|
||||
li x9, 134
|
||||
li x10, 906732
|
||||
li x11, 29
|
||||
li x12, 50912
|
||||
li x13, 59
|
||||
li x14, 6902385
|
||||
li x15, 1923857
|
||||
li x16, 3985
|
||||
li x17, 3947
|
||||
li x18, 15984
|
||||
li x19, 5
|
||||
li x20, 9684658489
|
||||
li x21, 6548
|
||||
li x22, 3564
|
||||
li x23, 94
|
||||
li x24, 689464
|
||||
li x25, 42567
|
||||
li x26, 98453
|
||||
li x27, 648
|
||||
li x28, 984
|
||||
li x29, 6984
|
||||
li x30, 864
|
||||
|
||||
# x31 will be our loop counter
|
||||
li x31, 4
|
||||
|
||||
.align 12
|
||||
jump1:
|
||||
divuw x0, x1, x2
|
||||
j jump3
|
||||
jump4:
|
||||
divuw x0, x5, x6
|
||||
j jump5
|
||||
jump6:
|
||||
divuw x0, x10, x9
|
||||
j jump7
|
||||
jump8:
|
||||
divuw x0, x14, x3
|
||||
j jump9
|
||||
jump10:
|
||||
divuw x0, x18, x17
|
||||
j jump11
|
||||
jump12:
|
||||
divuw x0, x21, x22
|
||||
j jump13
|
||||
jump14:
|
||||
divuw x0, x24, x25
|
||||
j jump15
|
||||
jump16:
|
||||
divuw x0, x29, x28
|
||||
j jump17
|
||||
jump18:
|
||||
divuw x0, x1, x30
|
||||
j jump19
|
||||
jump20:
|
||||
divuw x0, x3, x19
|
||||
j jump21
|
||||
jump22:
|
||||
divuw x0, x12, x13
|
||||
j jump23
|
||||
|
||||
.align 12 # size of the 1024 btb apart
|
||||
jump2:
|
||||
j jump1
|
||||
jump3:
|
||||
divuw x0, x4, x3
|
||||
j jump4
|
||||
jump5:
|
||||
divuw x0, x7, x8
|
||||
j jump6
|
||||
jump7:
|
||||
divuw x0, x12, x11
|
||||
j jump8
|
||||
jump9:
|
||||
divuw x0, x15, x16
|
||||
j jump10
|
||||
jump11:
|
||||
divuw x0, x20, x19
|
||||
j jump12
|
||||
jump13:
|
||||
divuw x0, x24, x23
|
||||
j jump14
|
||||
jump15:
|
||||
divuw x0, x26, x27
|
||||
j jump16
|
||||
jump17:
|
||||
divuw x0, x29, x30
|
||||
j jump18
|
||||
jump19:
|
||||
divuw x0, x2, x3
|
||||
j jump20
|
||||
jump21:
|
||||
divuw x0, x4, x5
|
||||
j jump22
|
||||
jump23:
|
||||
divuw x0, x20, x21
|
||||
#j jump22
|
||||
|
||||
fence.i
|
||||
|
||||
addi x31, x31, -1
|
||||
bne x31, x0, jump1
|
||||
finsihed:
|
||||
j done
|
||||
|
@ -238,46 +238,6 @@
|
||||
|
||||
0000001F
|
||||
|
||||
00000062 # hardware interlock
|
||||
|
||||
00000026
|
||||
|
||||
000000D2
|
||||
|
||||
0000002D
|
||||
|
||||
00000048
|
||||
|
||||
00000037
|
||||
|
||||
00000026
|
||||
|
||||
00000015
|
||||
|
||||
00000084
|
||||
|
||||
00000073
|
||||
|
||||
00000062
|
||||
|
||||
00000051
|
||||
|
||||
00000046
|
||||
|
||||
00000035
|
||||
|
||||
00000024
|
||||
|
||||
00000013
|
||||
|
||||
00000064
|
||||
|
||||
00000053
|
||||
|
||||
00000042
|
||||
|
||||
00000031
|
||||
|
||||
00000001 #watermark interrupts
|
||||
|
||||
00000000 #read mip
|
||||
|
@ -547,108 +547,13 @@ test_cases:
|
||||
#.4byte 0x0, 0x00000000, spi_data_wait # wait for transmission to end
|
||||
#.4byte rx_data, 0x000000F0, read32_test # read rx_data
|
||||
|
||||
#=========== Test Hardware Interlock ================
|
||||
|
||||
# interlock in base case
|
||||
|
||||
.4byte fmt, 0x00080000, write32_test # reset fmt register
|
||||
.4byte rx_mark, 0x0000001, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.4byte tx_data, 0x00000062, write32_test # initiate transmission
|
||||
.4byte sck_mode, 0x00000002, write32_test # flip polarity during transmission
|
||||
.4byte tx_data, 0x00000026, write32_test # transmit second frame w/ control register updated
|
||||
.4byte 0x0, 0x00000001, spi_data_wait
|
||||
.4byte rx_data, 0x00000062, read32_test
|
||||
.4byte rx_data, 0x00000026, read32_test # clear rx fifo
|
||||
.4byte sck_mode, 0x00000000, write32_test # reset polarity
|
||||
|
||||
# interlock in case where cs_mode is auto, but there is minimal intercs delay
|
||||
|
||||
.4byte delay0, 0x00000001, write32_test # set sck-cs delay to 0, with sck.pha 0 there is 0 delay
|
||||
.4byte tx_data, 0x000000D2, write32_test # initiate transmission
|
||||
.4byte sck_mode, 0x00000002, write32_test # flip sck polarity
|
||||
.4byte tx_data, 0x0000002D, write32_test # transmit second frame
|
||||
.4byte 0x0, 0x00000001, spi_data_wait
|
||||
.4byte rx_data, 0x000000D2, read32_test
|
||||
.4byte rx_data, 0x0000002D, read32_test # clear rx fifo
|
||||
.4byte sck_mode, 0x00000000, write32_test # reset polarity
|
||||
|
||||
# interlock in case where cs_mode = hold, 0 intercs delay
|
||||
.4byte delay0, 0x00010001, write32_test # reset delay0
|
||||
.4byte sck_mode, 0x00000000, write32_test # reset polarity
|
||||
.4byte cs_mode, 0x00000002, write32_test # set cs_mode to hold
|
||||
.4byte tx_data, 0x15263748, spi_burst_send # place 4 frames into tx fifo
|
||||
.4byte sck_mode, 0x00000002, write32_test # flip polarity (should change 2 second frame)
|
||||
.4byte 0x0, 0x00000001, spi_data_wait # wait for second transmission to end
|
||||
.4byte rx_mark, 0x0000003, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.4byte sck_mode, 0x00000000, write32_test # flip polarity again
|
||||
.4byte 0x0, 0x00000003, spi_data_wait # wait for final frame
|
||||
.4byte rx_data, 0x00000048, read32_test
|
||||
.4byte rx_data, 0x00000037, read32_test
|
||||
.4byte rx_data, 0x00000026, read32_test
|
||||
.4byte rx_data, 0x00000015, read32_test #clear rx fifo
|
||||
|
||||
# interlock in case where cs_mode = hold, intercs delay
|
||||
|
||||
.4byte sck_mode, 0x00000000, write32_test # reset polarity
|
||||
.4byte delay1, 0x00010001, write32_test # set intercs delay to 1
|
||||
.4byte rx_mark, 0x0000001, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.4byte tx_data, 0x51627384, spi_burst_send # place 4 frames into tx fifo
|
||||
.4byte sck_mode, 0x00000002, write32_test # flip polarity (should change 2 second frame)
|
||||
.4byte 0x0, 0x00000001, spi_data_wait # wait for second transmission to end
|
||||
.4byte rx_mark, 0x0000003, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.4byte sck_mode, 0x00000000, write32_test # flip polarity again
|
||||
.4byte 0x0, 0x00000003, spi_data_wait # wait for final frame
|
||||
.4byte rx_data, 0x00000084, read32_test
|
||||
.4byte rx_data, 0x00000073, read32_test
|
||||
.4byte rx_data, 0x00000062, read32_test
|
||||
.4byte rx_data, 0x00000051, read32_test #clear rx fifo
|
||||
|
||||
# repeat previous set of tests with cs_mode = off
|
||||
|
||||
.4byte cs_mode, 0x00000003, write32_test # set cs_mode to hold
|
||||
.4byte rx_mark, 0x0000001, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.4byte tx_data, 0x13243546, spi_burst_send # place 4 frames into tx fifo
|
||||
.4byte sck_mode, 0x00000002, write32_test # flip polarity (should change 2 second frame)
|
||||
.4byte 0x0, 0x00000001, spi_data_wait # wait for second transmission to end
|
||||
.4byte rx_mark, 0x0000003, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.4byte sck_mode, 0x00000000, write32_test # flip polarity again
|
||||
.4byte 0x0, 0x00000003, spi_data_wait # wait for final frame
|
||||
.4byte rx_data, 0x00000046, read32_test
|
||||
.4byte rx_data, 0x00000035, read32_test
|
||||
.4byte rx_data, 0x00000024, read32_test
|
||||
.4byte rx_data, 0x00000013, read32_test #clear rx fifo
|
||||
|
||||
# interlock in case where cs_mode = hold, intercs delay
|
||||
|
||||
.4byte sck_mode, 0x00000000, write32_test # reset polarity
|
||||
.4byte delay1, 0x00000000, write32_test # set intercs delay to 0
|
||||
.4byte rx_mark, 0x0000001, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.4byte tx_data, 0x31425364, spi_burst_send # place 4 frames into tx fifo
|
||||
.4byte sck_mode, 0x00000002, write32_test # flip polarity (should change 2 second frame)
|
||||
.4byte 0x0, 0x00000001, spi_data_wait # wait for second transmission to end
|
||||
.4byte rx_mark, 0x0000003, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.4byte sck_mode, 0x00000000, write32_test # flip polarity again
|
||||
.4byte 0x0, 0x00000003, spi_data_wait # wait for final frame
|
||||
.4byte rx_data, 0x00000064, read32_test
|
||||
.4byte rx_data, 0x00000053, read32_test
|
||||
.4byte rx_data, 0x00000042, read32_test
|
||||
.4byte rx_data, 0x00000031, read32_test #clear rx fifo
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# =========== Test watermark interrupts ===========
|
||||
|
||||
# Test transmit watermark interrupt (triggers when entries in tx FIFO < tx watermark) without external enables
|
||||
|
||||
SETUP_PLIC
|
||||
|
||||
.4byte fmt, 0x00080000, write32_test # reset format register
|
||||
.4byte delay1, 0x0000001, write32_test # reset delay1 register
|
||||
.4byte cs_mode, 0x00000000, write32_test # reset cs_mode
|
||||
.4byte tx_mark, 0x00000001, write32_test # set transmit watermark to 1 (any entry turns mark off)
|
||||
|
@ -238,46 +238,6 @@
|
||||
00000000
|
||||
0000001F
|
||||
00000000
|
||||
00000062 # hardware interlock
|
||||
00000000
|
||||
00000026
|
||||
00000000
|
||||
000000D2
|
||||
00000000
|
||||
0000002D
|
||||
00000000
|
||||
00000048
|
||||
00000000
|
||||
00000037
|
||||
00000000
|
||||
00000026
|
||||
00000000
|
||||
00000015
|
||||
00000000
|
||||
00000084
|
||||
00000000
|
||||
00000073
|
||||
00000000
|
||||
00000062
|
||||
00000000
|
||||
00000051
|
||||
00000000
|
||||
00000046
|
||||
00000000
|
||||
00000035
|
||||
00000000
|
||||
00000024
|
||||
00000000
|
||||
00000013
|
||||
00000000
|
||||
00000064
|
||||
00000000
|
||||
00000053
|
||||
00000000
|
||||
00000042
|
||||
00000000
|
||||
00000031
|
||||
00000000
|
||||
00000001 #watermark interrupts
|
||||
00000000
|
||||
00000000 #read mip
|
||||
|
@ -551,94 +551,6 @@ test_cases:
|
||||
#.8byte 0x0, 0x00000000, spi_data_wait # wait for transmission to end
|
||||
#.8byte rx_data, 0x000000F0, read32_test # read rx_data
|
||||
|
||||
#=========== Test Hardware Interlock ================
|
||||
|
||||
# interlock in base case
|
||||
|
||||
.8byte fmt, 0x00080000, write32_test # reset fmt register
|
||||
.8byte rx_mark, 0x0000001, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.8byte tx_data, 0x00000062, write32_test # initiate transmission
|
||||
.8byte sck_mode, 0x00000002, write32_test # flip polarity during transmission
|
||||
.8byte tx_data, 0x00000026, write32_test # transmit second frame w/ control register updated
|
||||
.8byte 0x0, 0x00000001, spi_data_wait
|
||||
.8byte rx_data, 0x00000062, read32_test
|
||||
.8byte rx_data, 0x00000026, read32_test # clear rx fifo
|
||||
.8byte sck_mode, 0x00000000, write32_test # reset polarity
|
||||
|
||||
# interlock in case where cs_mode is auto, but there is minimal intercs delay
|
||||
|
||||
.8byte delay0, 0x00000001, write32_test # set sck-cs delay to 0, with sck.pha 0 there is 0 delay
|
||||
.8byte tx_data, 0x000000D2, write32_test # initiate transmission
|
||||
.8byte sck_mode, 0x00000002, write32_test # flip sck polarity
|
||||
.8byte tx_data, 0x0000002D, write32_test # transmit second frame
|
||||
.8byte 0x0, 0x00000001, spi_data_wait
|
||||
.8byte rx_data, 0x000000D2, read32_test
|
||||
.8byte rx_data, 0x0000002D, read32_test # clear rx fifo
|
||||
.8byte sck_mode, 0x00000000, write32_test # reset polarity
|
||||
|
||||
# interlock in case where cs_mode = hold, 0 intercs delay
|
||||
.8byte delay0, 0x00010001, write32_test # reset delay0
|
||||
.8byte sck_mode, 0x00000000, write32_test # reset polarity
|
||||
.8byte cs_mode, 0x00000002, write32_test # set cs_mode to hold
|
||||
.8byte tx_data, 0x15263748, spi_burst_send # place 4 frames into tx fifo
|
||||
.8byte sck_mode, 0x00000002, write32_test # flip polarity (should change 2 second frame)
|
||||
.8byte 0x0, 0x00000001, spi_data_wait # wait for second transmission to end
|
||||
.8byte rx_mark, 0x0000003, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.8byte sck_mode, 0x00000000, write32_test # flip polarity again
|
||||
.8byte 0x0, 0x00000003, spi_data_wait # wait for final frame
|
||||
.8byte rx_data, 0x00000048, read32_test
|
||||
.8byte rx_data, 0x00000037, read32_test
|
||||
.8byte rx_data, 0x00000026, read32_test
|
||||
.8byte rx_data, 0x00000015, read32_test #clear rx fifo
|
||||
|
||||
# interlock in case where cs_mode = hold, intercs delay
|
||||
|
||||
.8byte sck_mode, 0x00000000, write32_test # reset polarity
|
||||
.8byte delay1, 0x00010001, write32_test # set intercs delay to 1
|
||||
.8byte rx_mark, 0x0000001, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.8byte tx_data, 0x51627384, spi_burst_send # place 4 frames into tx fifo
|
||||
.8byte sck_mode, 0x00000002, write32_test # flip polarity (should change 2 second frame)
|
||||
.8byte 0x0, 0x00000001, spi_data_wait # wait for second transmission to end
|
||||
.8byte rx_mark, 0x0000003, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.8byte sck_mode, 0x00000000, write32_test # flip polarity again
|
||||
.8byte 0x0, 0x00000003, spi_data_wait # wait for final frame
|
||||
.8byte rx_data, 0x00000084, read32_test
|
||||
.8byte rx_data, 0x00000073, read32_test
|
||||
.8byte rx_data, 0x00000062, read32_test
|
||||
.8byte rx_data, 0x00000051, read32_test #clear rx fifo
|
||||
|
||||
# repeat previous set of tests with cs_mode = off
|
||||
|
||||
.8byte cs_mode, 0x00000003, write32_test # set cs_mode to hold
|
||||
.8byte rx_mark, 0x0000001, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.8byte tx_data, 0x13243546, spi_burst_send # place 4 frames into tx fifo
|
||||
.8byte sck_mode, 0x00000002, write32_test # flip polarity (should change 2 second frame)
|
||||
.8byte 0x0, 0x00000001, spi_data_wait # wait for second transmission to end
|
||||
.8byte rx_mark, 0x0000003, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.8byte sck_mode, 0x00000000, write32_test # flip polarity again
|
||||
.8byte 0x0, 0x00000003, spi_data_wait # wait for final frame
|
||||
.8byte rx_data, 0x00000046, read32_test
|
||||
.8byte rx_data, 0x00000035, read32_test
|
||||
.8byte rx_data, 0x00000024, read32_test
|
||||
.8byte rx_data, 0x00000013, read32_test #clear rx fifo
|
||||
|
||||
# interlock in case where cs_mode = hold, intercs delay
|
||||
|
||||
.8byte sck_mode, 0x00000000, write32_test # reset polarity
|
||||
.8byte delay1, 0x00000000, write32_test # set intercs delay to 0
|
||||
.8byte rx_mark, 0x0000001, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.8byte tx_data, 0x31425364, spi_burst_send # place 4 frames into tx fifo
|
||||
.8byte sck_mode, 0x00000002, write32_test # flip polarity (should change 2 second frame)
|
||||
.8byte 0x0, 0x00000001, spi_data_wait # wait for second transmission to end
|
||||
.8byte rx_mark, 0x0000003, write32_test # preset rx watermark b/c of hardware interlock
|
||||
.8byte sck_mode, 0x00000000, write32_test # flip polarity again
|
||||
.8byte 0x0, 0x00000003, spi_data_wait # wait for final frame
|
||||
.8byte rx_data, 0x00000064, read32_test
|
||||
.8byte rx_data, 0x00000053, read32_test
|
||||
.8byte rx_data, 0x00000042, read32_test
|
||||
.8byte rx_data, 0x00000031, read32_test #clear rx fifo
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -652,7 +564,7 @@ test_cases:
|
||||
# Test transmit watermark interrupt (triggers when entries in tx FIFO < tx watermark) without external enables
|
||||
|
||||
SETUP_PLIC
|
||||
|
||||
.8byte fmt, 0x00080000, write32_test # reset format register
|
||||
.8byte delay1, 0x0000001, write32_test # reset delay1 register
|
||||
.8byte cs_mode, 0x00000000, write32_test # reset cs_mode
|
||||
.8byte sck_div, 0x00000100, write32_test # lower SPI clock rate so reads are done at correct time when ICACHE not supported
|
||||
|
Loading…
Reference in New Issue
Block a user