From 6bec559ba60d568493e33c3e7b7435232fc49dee Mon Sep 17 00:00:00 2001 From: David Harris Date: Tue, 21 Nov 2023 19:49:14 -0800 Subject: [PATCH 01/48] Removed stale signals from wave.do --- sim/wave.do | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sim/wave.do b/sim/wave.do index 3f2bcd72a..3a5fd9369 100644 --- a/sim/wave.do +++ b/sim/wave.do @@ -120,7 +120,6 @@ add wave -noupdate -expand -group lsu -group alignment /testbench/dut/core/lsu/z add wave -noupdate -expand -group lsu -group alignment /testbench/dut/core/lsu/ziccslm_align/align/ByteMaskMuxM add wave -noupdate -expand -group lsu -group alignment /testbench/dut/core/lsu/ByteMaskSpillM add wave -noupdate -expand -group lsu -group alignment /testbench/dut/core/lsu/LSUWriteDataM -add wave -noupdate -expand -group lsu -group alignment /testbench/dut/core/lsu/ziccslm_align/align/LSUWriteDataShiftedM add wave -noupdate -expand -group lsu -group alignment /testbench/dut/core/lsu/LSUWriteDataSpillM add wave -noupdate -expand -group lsu -group alignment /testbench/dut/core/lsu/bus/dcache/dcache/CacheWriteData add wave -noupdate -expand -group lsu -group alignment /testbench/dut/core/lsu/bus/dcache/dcache/ByteMask @@ -128,14 +127,12 @@ add wave -noupdate -expand -group lsu -group alignment /testbench/dut/core/lsu/b add wave -noupdate -expand -group lsu -group alignment /testbench/dut/core/lsu/bus/dcache/dcache/WriteSelLogic/DemuxedByteMask add wave -noupdate -expand -group lsu -group alignment /testbench/dut/core/lsu/bus/dcache/dcache/WriteSelLogic/FetchBufferByteSel add wave -noupdate -expand -group lsu -group alignment {/testbench/dut/core/lsu/bus/dcache/dcache/CacheWays[2]/LineWriteData} -add wave -noupdate -expand -group lsu -group alignment /testbench/dut/core/lsu/ziccslm_align/align/IncrementAmount add wave -noupdate -expand -group lsu /testbench/dut/core/lsu/IEUAdrExtE add wave -noupdate -expand -group lsu /testbench/dut/core/lsu/IEUAdrExtM add wave -noupdate -expand -group lsu /testbench/dut/core/lsu/bus/dcache/dcache/NextSet add wave -noupdate -expand -group lsu -expand -group dcache /testbench/dut/core/lsu/bus/dcache/dcache/CacheHit add wave -noupdate -expand -group lsu -expand -group dcache /testbench/dut/core/lsu/bus/dcache/dcache/CacheRW add wave -noupdate -expand -group lsu -expand -group dcache /testbench/dut/core/lsu/bus/dcache/dcache/CMOp -add wave -noupdate -expand -group lsu -expand -group dcache /testbench/dut/core/lsu/bus/dcache/dcache/CMOZeroHit add wave -noupdate -expand -group lsu -expand -group dcache -color Gold /testbench/dut/core/lsu/bus/dcache/dcache/cachefsm/CurrState add wave -noupdate -expand -group lsu -expand -group dcache /testbench/dut/core/lsu/bus/dcache/dcache/HitWay add wave -noupdate -expand -group lsu -expand -group dcache /testbench/dut/core/lsu/bus/dcache/dcache/SetValid @@ -212,7 +209,6 @@ add wave -noupdate -expand -group lsu -expand -group dcache -group {Cache SRAM w add wave -noupdate -expand -group lsu -expand -group dcache -group {Cache SRAM writes} -expand -group way0 -expand -group Way0Word3 {/testbench/dut/core/lsu/bus/dcache/dcache/CacheWays[0]/word[3]/wordram/CacheDataMem/we} add wave -noupdate -expand -group lsu -expand -group dcache -group {Cache SRAM writes} -expand -group way0 -expand -group Way0Word3 {/testbench/dut/core/lsu/bus/dcache/dcache/CacheWays[0]/word[3]/wordram/CacheDataMem/bwe} add wave -noupdate -expand -group lsu -expand -group dcache -group {Cache SRAM writes} -expand -group way0 -expand -group Way0Word3 {/testbench/dut/core/lsu/bus/dcache/dcache/CacheWays[0]/word[3]/wordram/CacheDataMem/RAM} -add wave -noupdate -expand -group lsu -expand -group dcache -group {Cache SRAM writes} -group way1 {/testbench/dut/core/lsu/bus/dcache/dcache/CacheWays[1]/SelNotHit2} add wave -noupdate -expand -group lsu -expand -group dcache -group {Cache SRAM writes} -group way1 {/testbench/dut/core/lsu/bus/dcache/dcache/CacheWays[1]/SelNonHit} add wave -noupdate -expand -group lsu -expand -group dcache -group {Cache SRAM writes} -group way1 {/testbench/dut/core/lsu/bus/dcache/dcache/CacheWays[1]/SelData} add wave -noupdate -expand -group lsu -expand -group dcache -group {Cache SRAM writes} -group way1 {/testbench/dut/core/lsu/bus/dcache/dcache/CacheWays[1]/SelectedWriteWordEn} From d1bb5c7512957b3bc4c1cf5956526182ddbfe254 Mon Sep 17 00:00:00 2001 From: David Harris Date: Tue, 21 Nov 2023 21:52:11 -0800 Subject: [PATCH 02/48] Imperas fix for satp modes supported --- linux/Makefile | 3 ++- sim/imperas.ic | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/linux/Makefile b/linux/Makefile index 520c7cd44..a2cded5ea 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -39,6 +39,7 @@ Image: # source ../setup.sh; $(MAKE) disassemble install: + sudo rm -rf $(RISCV)/$(BUILDROOT) sudo mv $(BUILDROOT) $(RISCV)/$(BUILDROOT) # Temp rule for debugging @@ -62,10 +63,10 @@ $(RISCV): disassemble: + rm -rf $(BUILDROOT)/output/images/disassembly find $(BUILDROOT)/output/build/linux-* -maxdepth 1 -name "vmlinux" | xargs cp -t $(BUILDROOT)/output/images/ mkdir -p $(DIS) make -j $(OBJDUMPS) - make -j $(OBJDUMPS).addr $(DIS)/%.objdump: $(IMAGES)/%.elf riscv64-unknown-elf-objdump -DS $< >> $@ diff --git a/sim/imperas.ic b/sim/imperas.ic index 4106968ed..be6b7cca6 100644 --- a/sim/imperas.ic +++ b/sim/imperas.ic @@ -32,6 +32,9 @@ --override cpu/Svpbmt=T --override cpu/Svnapot_page_mask=65536 +# SV39 and SV48 supported +--override cpu/Sv_modes=768 + # clarify #--override refRoot/cpu/mtvec_sext=F From 35d30a5a31b82251d5b479b55e050060a199f553 Mon Sep 17 00:00:00 2001 From: Andrei Solodovnikov Date: Wed, 22 Nov 2023 15:23:03 +0300 Subject: [PATCH 03/48] Fix testplan link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0dc68f1c1..b9d378fc3 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Wally is described in an upcoming textbook, *RISC-V System-on-Chip Design*, by H # Verification -Wally is presently at Technology Readiness Level 4, passing the RISC-V compatibility test suite and custom tests, and booting Linux in simulation and on an FPGA. See the [Test Plan](docs/testplan.md) for details. +Wally is presently at Technology Readiness Level 4, passing the RISC-V compatibility test suite and custom tests, and booting Linux in simulation and on an FPGA. See the [Test Plan](docs/testplans/testplan.md) for details. # New User Setup From e66adcca9dc3d03949f163236f6f09bbe45a7c23 Mon Sep 17 00:00:00 2001 From: David Harris Date: Wed, 22 Nov 2023 05:25:09 -0800 Subject: [PATCH 04/48] Cleaned up genInitMem script to only generate necessary files and eliminate prompts --- linux/testvector-generation/genInitMem.sh | 109 ++++++++++------------ 1 file changed, 50 insertions(+), 59 deletions(-) diff --git a/linux/testvector-generation/genInitMem.sh b/linux/testvector-generation/genInitMem.sh index d34b758f8..b9c1d8c41 100755 --- a/linux/testvector-generation/genInitMem.sh +++ b/linux/testvector-generation/genInitMem.sh @@ -10,63 +10,54 @@ rawUntrimmedBootmemFile="$tvDir/untrimmedBootmemFileGDB.bin" untrimmedBootmemFile="$tvDir/untrimmedBootmemFile.bin" DEVICE_TREE=${imageDir}/wally-virt.dtb -read -p "Warning: running this script will overwrite the contents of: - * $rawRamFile - * $ramFile - * $rawBootmemFile - * $bootmemFile - * $rawUntrimmedBootmemFile - * $untrimmedBootmemFile -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 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 \ - -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 "printf \"Creating $rawBootmemFile\n\"" \ - -ex "dump binary memory $rawBootmemFile 0x1000 0x1fff" \ - -ex "printf \"Creating $rawUntrimmedBootmemFile\n\"" \ - -ex "printf \"Warning - please verify that the second half of $rawUntrimmedBootmemFile is all 0s\n\"" \ - -ex "dump binary memory $rawUntrimmedBootmemFile 0x1000 0x2fff" \ - -ex "printf \"Creating $rawRamFile\n\"" \ - -ex "dump binary memory $rawRamFile 0x80000000 0x8fffffff" \ - -ex "kill" \ - -ex "q" - - echo "Changing Endianness" - make fixBinMem - ./fixBinMem "$rawRamFile" "$ramFile" - ./fixBinMem "$rawBootmemFile" "$bootmemFile" - ./fixBinMem "$rawUntrimmedBootmemFile" "$untrimmedBootmemFile" - - echo "genInitMem.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" +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 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 \ +-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 "printf \"Creating $rawBootmemFile\n\"" \ +-ex "dump binary memory $rawBootmemFile 0x1000 0x1fff" \ +-ex "printf \"Creating $rawRamFile\n\"" \ +-ex "dump binary memory $rawRamFile 0x80000000 0x8fffffff" \ +-ex "kill" \ +-ex "q" + +#-ex "printf \"Warning - please verify that the second half of $rawUntrimmedBootmemFile is all 0s\n\"" \ +#-ex "printf \"Creating $rawUntrimmedBootmemFile\n\"" \ +#-ex "dump binary memory $rawUntrimmedBootmemFile 0x1000 0x2fff" \ + +echo "Changing Endianness" +make fixBinMem +./fixBinMem "$rawRamFile" "$ramFile" +./fixBinMem "$rawBootmemFile" "$bootmemFile" +#./fixBinMem "$rawUntrimmedBootmemFile" "$untrimmedBootmemFile" # doesn't seem to be used for anything +rm -f "$rawRamFile" "$rawBootmemFile" "$rawUntrimmedBootmemFile" + +echo "genInitMem.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" + From c64ad9ff355db32e521b2b5af86c0e7c0db6e92a Mon Sep 17 00:00:00 2001 From: David Harris Date: Wed, 22 Nov 2023 06:28:38 -0800 Subject: [PATCH 05/48] Extract rootfs during disassembly --- linux/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/linux/Makefile b/linux/Makefile index a2cded5ea..cc19c7f2a 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -67,6 +67,10 @@ disassemble: find $(BUILDROOT)/output/build/linux-* -maxdepth 1 -name "vmlinux" | xargs cp -t $(BUILDROOT)/output/images/ mkdir -p $(DIS) make -j $(OBJDUMPS) + # extract rootfs + mkdir -p $(BUILDROOT)/output/images/disassembly/rootfs + echo "Ignore error about dev/console when extracting rootfs from rootfs.cpio" + -cpio -i -D $(BUILDROOT)/output/images/disassembly/rootfs < $(BUILDROOT)/output/images/rootfs.cpio $(DIS)/%.objdump: $(IMAGES)/%.elf riscv64-unknown-elf-objdump -DS $< >> $@ From 1d234c05c99bb1679c8a26140915cb90b7a22cf6 Mon Sep 17 00:00:00 2001 From: David Harris Date: Wed, 22 Nov 2023 22:17:01 -0800 Subject: [PATCH 06/48] disassembleBootTrace works on first 50M lines of boot --- .gitignore | 2 + linux/bootmem.txt | 2 +- .../disassembleBootTrace.py | 74 +++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100755 linux/testvector-generation/disassembleBootTrace.py diff --git a/.gitignore b/.gitignore index 3990c3823..f0afc3a98 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,8 @@ tests/linux-testgen/buildroot-image-output tests/linux-testgen/buildroot-config-src/main.config.old tests/linux-testgen/buildroot-config-src/linux.config.old tests/linux-testgen/buildroot-config-src/busybox.config.old +linux/testvector-generation/boottrace.S +linux/testvector-generation/boottrace_disasm.log sim/slack-notifier/slack-webhook-url.txt sim/logs fpga/generator/IP diff --git a/linux/bootmem.txt b/linux/bootmem.txt index 047cf5529..7cf7d8453 100644 --- a/linux/bootmem.txt +++ b/linux/bootmem.txt @@ -5,7 +5,7 @@ 00001010: 0182b283 ld t0, 24(t0) # t0 = 80000000 - start of firmware 00001014: 00028067 jr t0 # jump to firmware 00001018: 0000000080000000 # firmware start address -00001020: 0000000087000000 # flattened device tree load address +00001020: 000000008fe00000 # flattened device tree load address 00001028: 000000004942534f # a2 points to this 8 dword data structure 00001030: 0000000000000002 00001038: 0000000080200000 diff --git a/linux/testvector-generation/disassembleBootTrace.py b/linux/testvector-generation/disassembleBootTrace.py new file mode 100755 index 000000000..12e2202cb --- /dev/null +++ b/linux/testvector-generation/disassembleBootTrace.py @@ -0,0 +1,74 @@ +#!/usr/bin/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() From 1ab75220642fa06b2ef5239d1c1292bdd7c94d58 Mon Sep 17 00:00:00 2001 From: "James E. Stine" Date: Thu, 23 Nov 2023 17:56:51 -0600 Subject: [PATCH 07/48] Update fix for cvtint testbench-fp --- sim/testfloat.do | 2 +- testbench/testbench-fp.sv | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sim/testfloat.do b/sim/testfloat.do index 85bd6dfc5..f21975389 100644 --- a/sim/testfloat.do +++ b/sim/testfloat.do @@ -28,7 +28,7 @@ vlib work vlog +incdir+../config/$1 +incdir+../config/shared ../src/cvw.sv ../testbench/testbench-fp.sv ../src/fpu/*.sv ../src/fpu/*/*.sv ../src/generic/*.sv ../src/generic/flop/*.sv -suppress 2583,7063,8607,2697 # Change TEST_SIZE to only test certain FP width -# values are QP, DP, SP, HP +# values are QP, DP, SP, HP or all for all tests vsim -voptargs=+acc work.testbenchfp -GTEST=$2 -GTEST_SIZE="all" # Set WAV variable to avoid having any output to wave (to limit disk space) diff --git a/testbench/testbench-fp.sv b/testbench/testbench-fp.sv index e5f215e07..84b1ae3c7 100644 --- a/testbench/testbench-fp.sv +++ b/testbench/testbench-fp.sv @@ -964,11 +964,16 @@ module testbenchfp; // Testfloat outputs 800... for both the largest integer values for both positive and negitive numbers but // the riscv spec specifies 2^31-1 for positive values out of range and NaNs ie 7fff... + + // Note: Went through and determined that this is not needed with new module additions + // Just needs to check flags against TestFloat (left just in case (remove after check one more time)) + // else if ((UnitVal === `CVTINTUNIT) & + // ~(((WriteIntVal&~OpCtrlVal[0]&AnsFlg[4]&Xs&(Res[P.XLEN-1:0] === (P.XLEN)'(0))) | + // (WriteIntVal&OpCtrlVal[0]&AnsFlg[4]&(~Xs|XNaN)&OpCtrlVal[1]&(Res[P.XLEN-1:0] === {1'b0, {P.XLEN-1{1'b1}}})) | + // (WriteIntVal&OpCtrlVal[0]&AnsFlg[4]&(~Xs|XNaN)&~OpCtrlVal[1]&(Res[P.XLEN-1:0] === {{P.XLEN-32{1'b0}}, 1'b0, {31{1'b1}}})) | + // (~(WriteIntVal&~OpCtrlVal[0]&AnsFlg[4]&Xs&~XNaN)&(Res === Ans | NaNGood | NaNGood === 1'bx))) & (ResFlg === AnsFlg | AnsFlg === 5'bx))) begin else if ((UnitVal === `CVTINTUNIT) & - ~(((WriteIntVal&~OpCtrlVal[0]&AnsFlg[4]&Xs&(Res[P.XLEN-1:0] === (P.XLEN)'(0))) | - (WriteIntVal&OpCtrlVal[0]&AnsFlg[4]&(~Xs|XNaN)&OpCtrlVal[1]&(Res[P.XLEN-1:0] === {1'b0, {P.XLEN-1{1'b1}}})) | - (WriteIntVal&OpCtrlVal[0]&AnsFlg[4]&(~Xs|XNaN)&~OpCtrlVal[1]&(Res[P.XLEN-1:0] === {{P.XLEN-32{1'b0}}, 1'b0, {31{1'b1}}})) | - (~(WriteIntVal&~OpCtrlVal[0]&AnsFlg[4]&Xs&~XNaN)&(Res === Ans | NaNGood | NaNGood === 1'bx))) & (ResFlg === AnsFlg | AnsFlg === 5'bx))) begin + ~((ResFlg === AnsFlg | AnsFlg === 5'bx))) begin errors += 1; $display("There is an error in %s", Tests[TestNum]); $display("inputs: %h %h %h\nSrcA: %h\n Res: %h %h\n Ans: %h %h", X, Y, Z, SrcA, Res, ResFlg, Ans, AnsFlg); From 1f57df7f8b378cea00e66cd82d962df5af8d4d20 Mon Sep 17 00:00:00 2001 From: David Harris Date: Thu, 23 Nov 2023 20:29:10 -0800 Subject: [PATCH 08/48] Fixed reference to deleted atomic signal in cache --- src/lsu/lsu.sv | 4 ++-- testbench/common/loggers.sv | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lsu/lsu.sv b/src/lsu/lsu.sv index 396fa1515..5c21d7ecd 100644 --- a/src/lsu/lsu.sv +++ b/src/lsu/lsu.sv @@ -196,8 +196,8 @@ module lsu import cvw::*; #(parameter cvw_t P) ( assign PreLSURWM = MemRWM; assign IHAdrM = IEUAdrExtM; assign LSUFunct3M = Funct3M; - assign LSUFunct7M = Funct7M; - assign LSUAtomicM = AtomicM; + assign LSUFunct7M = Funct7M; + assign LSUAtomicM = AtomicM; assign IHWriteDataM = WriteDataM; assign LoadAccessFaultM = LSULoadAccessFaultM; assign StoreAmoAccessFaultM = LSUStoreAmoAccessFaultM; diff --git a/testbench/common/loggers.sv b/testbench/common/loggers.sv index 1cf719085..e1cc1795f 100644 --- a/testbench/common/loggers.sv +++ b/testbench/common/loggers.sv @@ -182,7 +182,7 @@ module loggers import cvw::*; #(parameter cvw_t P, (!dut.core.lsu.bus.dcache.dcache.vict.cacheLRU.AllValid) ? "M" : dut.core.lsu.bus.dcache.dcache.LineDirty ? "D" : "E"; AccessTypeString = dut.core.lsu.bus.dcache.FlushDCache ? "F" : - dut.core.lsu.bus.dcache.CacheAtomicM[1] ? "A" : + dut.core.lsu.LSUAtomicM[1] ? "A" : dut.core.lsu.bus.dcache.CacheRWM == 2'b10 ? "R" : dut.core.lsu.bus.dcache.CacheRWM == 2'b01 ? "W" : "NULL"; From 3df4c13daa8fa7485c0f1f2dbdb08ed4ab1adfe7 Mon Sep 17 00:00:00 2001 From: David Harris Date: Thu, 23 Nov 2023 20:36:45 -0800 Subject: [PATCH 09/48] Updated wallyTracer for Linux boot and wally-batch.do to remove buildroot checkpoint support --- sim/wally-batch.do | 8 ++++---- testbench/common/wallyTracer.sv | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sim/wally-batch.do b/sim/wally-batch.do index 29d31bd25..df34aa0b3 100644 --- a/sim/wally-batch.do +++ b/sim/wally-batch.do @@ -58,15 +58,15 @@ if {$argc >= 3} { # default to config/rv64ic, but allow this to be overridden at the command line. For example: # do wally-pipelined-batch.do ../config/rv32imc rv32imc -if {$2 eq "buildroot" || $2 eq "buildroot-checkpoint"} { +if {$2 eq "buildroot"} { vlog -lint -work wkdir/work_${1}_${2} +incdir+../config/$1 +incdir+../config/shared ../src/cvw.sv ../testbench/testbench-linux.sv ../testbench/common/*.sv ../src/*/*.sv ../src/*/*/*.sv -suppress 2583 # start and run simulation if { $coverage } { echo "wally-batch buildroot coverage" - vopt wkdir/work_${1}_${2}.testbench -work wkdir/work_${1}_${2} -G RISCV_DIR=$3 -G INSTR_LIMIT=$4 -G INSTR_WAVEON=$5 -G CHECKPOINT=$6 -o testbenchopt +cover=sbecf + vopt wkdir/work_${1}_${2}.testbench -work wkdir/work_${1}_${2} -G RISCV_DIR=$3 -G INSTR_LIMIT=$4 -G INSTR_WAVEON=$5 -G -o testbenchopt +cover=sbecf vsim -lib wkdir/work_${1}_${2} testbenchopt -suppress 8852,12070,3084,3691,13286 -fatal 7 -cover } else { - vopt wkdir/work_${1}_${2}.testbench -work wkdir/work_${1}_${2} -G RISCV_DIR=$3 -G INSTR_LIMIT=$4 -G INSTR_WAVEON=$5 -G CHECKPOINT=$6 -o testbenchopt + vopt wkdir/work_${1}_${2}.testbench -work wkdir/work_${1}_${2} -G RISCV_DIR=$3 -G INSTR_LIMIT=$4 -G INSTR_WAVEON=$5 -G -o testbenchopt vsim -lib wkdir/work_${1}_${2} testbenchopt -suppress 8852,12070,3084,3691,13286 -fatal 7 } @@ -76,7 +76,7 @@ if {$2 eq "buildroot" || $2 eq "buildroot-checkpoint"} { } elseif {$2 eq "buildroot-no-trace"} { vlog -lint -work work_${1}_${2} +incdir+../config/$1 +incdir+../config/shared ../testbench/testbench-linux.sv ../testbench/common/*.sv ../src/*/*.sv ../src/*/*/*.sv -suppress 2583 # start and run simulation - vopt +acc work_${1}_${2}.testbench -work work_${1}_${2} -G RISCV_DIR=$3 -G INSTR_LIMIT=$4 -G INSTR_WAVEON=$5 -G CHECKPOINT=$6 -G NO_SPOOFING=1 -o testbenchopt + vopt +acc work_${1}_${2}.testbench -work work_${1}_${2} -G RISCV_DIR=$3 -G INSTR_LIMIT=$4 -G INSTR_WAVEON=$5 -G -G NO_SPOOFING=1 -o testbenchopt vsim -lib work_${1}_${2} testbenchopt -suppress 8852,12070,3084,3829,13286 -fatal 7 #-- Run the Simulation diff --git a/testbench/common/wallyTracer.sv b/testbench/common/wallyTracer.sv index 1ba7f010d..746fde068 100644 --- a/testbench/common/wallyTracer.sv +++ b/testbench/common/wallyTracer.sv @@ -23,7 +23,7 @@ `define NUM_REGS 32 `define NUM_CSRS 4096 -`define STD_LOG 0 +`define STD_LOG 1 `define PRINT_PC_INSTR 0 `define PRINT_MOST 0 `define PRINT_ALL 0 @@ -502,7 +502,7 @@ module wallyTracer import cvw::*; #(parameter cvw_t P) (rvviTrace rvvi); if(`STD_LOG) begin instrNameDecTB NameDecoder(rvvi.insn[0][0], instrWName); initial begin - LogFile = "logs/InstrTrace.log"; + LogFile = "logs/boottrace.log"; file = $fopen(LogFile, "w"); end end From 0c789c2fbda255884ebf0cbdc0189bff51eead73 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Sun, 26 Nov 2023 15:34:40 -0600 Subject: [PATCH 10/48] Fixed subtle bug in the embench makefile if the sim/wkdir directory did not exist. --- benchmarks/embench/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/benchmarks/embench/Makefile b/benchmarks/embench/Makefile index d7a18b7e2..d8fbddae9 100644 --- a/benchmarks/embench/Makefile +++ b/benchmarks/embench/Makefile @@ -43,6 +43,7 @@ sim: modelsim_build_memfile modelsim_run speed # launches modelsim to simulate tests on wally modelsim_run: + mkdir -p ../../sim/wkdir (cd ../../sim/ && vsim -c -do "do wally-batch.do rv32gc embench") cd ../../benchmarks/embench/ @@ -82,4 +83,4 @@ clean: allclean: clean rm -rf $(embench_dir)/logs/ -# riscv64-unknown-elf-gcc -O2 -g -nostartfiles -I/home/harris/riscv-wally/addins/embench-iot/support -I/home/harris/riscv-wally/addins/embench-iot/config/riscv32/boards/ri5cyverilator -I/home/harris/riscv-wally/addins/embench-iot/config/riscv32/chips/generic -I/home/harris/riscv-wally/addins/embench-iot/config/riscv32 -DCPU_MHZ=1 -DWARMUP_HEAT=1 -o main.o /home/harris/riscv-wally/addins/embench-iot/support/main.c \ No newline at end of file +# riscv64-unknown-elf-gcc -O2 -g -nostartfiles -I/home/harris/riscv-wally/addins/embench-iot/support -I/home/harris/riscv-wally/addins/embench-iot/config/riscv32/boards/ri5cyverilator -I/home/harris/riscv-wally/addins/embench-iot/config/riscv32/chips/generic -I/home/harris/riscv-wally/addins/embench-iot/config/riscv32 -DCPU_MHZ=1 -DWARMUP_HEAT=1 -o main.o /home/harris/riscv-wally/addins/embench-iot/support/main.c From e747f97dceb1ba23d45217f660cf508a06fe0e0c Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Sun, 26 Nov 2023 17:30:11 -0600 Subject: [PATCH 11/48] Fixed repeatability issues with the branch predictor simulator results generation. I reran using a clean clone of the repo so this should be working now. --- bin/CModelBTBAccuracy.sh | 8 ++++---- bin/CModelBranchAccuracy.sh | 2 +- bin/parseHPMC.py | 13 +++++++------ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/bin/CModelBTBAccuracy.sh b/bin/CModelBTBAccuracy.sh index e4a26fb85..479340eb2 100755 --- a/bin/CModelBTBAccuracy.sh +++ b/bin/CModelBTBAccuracy.sh @@ -40,18 +40,18 @@ do lines=`sim_bp gshare 16 16 $Size 1 $File | tail -5` Total=`echo "$lines" | head -1 | awk '{print $5}'` Miss=`echo "$lines" | tail -2 | head -1 | awk '{print $8}'` - BMDR=`echo "$Miss / $Total" | bc -l` + BMDR=`echo "100.0 * $Miss / $Total" | bc -l` BMDRArray+=("$BMDR") if [ $Miss -eq 0 ]; then - Product=`echo "scale=200; $Product / $Total" | bc -l` + Product=`echo "scale=200; $Product * 100 / $Total" | bc -l` else - Product=`echo "scale=200; $Product * $Miss / $Total" | bc -l` + Product=`echo "scale=200; $Product * $BMDR" | bc -l` fi Count=$((Count+1)) done # with such long precision bc outputs onto multiple lines # must remove \n and \ from string Product=`echo "$Product" | tr -d '\n' | tr -d '\\\'` - GeoMean=`perl -E "say $Product**(1/$Count) * 100"` + GeoMean=`perl -E "say $Product**(1/$Count)"` echo "$Pred$Size $GeoMean" done diff --git a/bin/CModelBranchAccuracy.sh b/bin/CModelBranchAccuracy.sh index 1b94f7c9a..8253891bb 100755 --- a/bin/CModelBranchAccuracy.sh +++ b/bin/CModelBranchAccuracy.sh @@ -46,7 +46,7 @@ do do #echo "sim_bp $Pred $Size $Size 18 1 $File | tail -1 | awk '{print $4}'" #echo "sim_bp $Pred $SizeString $File | tail -1 | awk '{print $4}'" - BMDR=`sim_bp $Pred $SizeString $File | tail -1 | awk '{print $4}'` + BMDR=`sim_bp -c $Pred $SizeString $File | tail -1 | awk '{print $4}'` Product=`echo "$Product * $BMDR" | bc` Count=$((Count+1)) done diff --git a/bin/parseHPMC.py b/bin/parseHPMC.py index a11296b3e..f9a961c18 100755 --- a/bin/parseHPMC.py +++ b/bin/parseHPMC.py @@ -32,12 +32,13 @@ import math import numpy as np import argparse -RefDataBP = [('twobitCModel6', 'twobitCModel', 64, 9.65280765420711), ('twobitCModel8', 'twobitCModel', 256, 8.75120245829945), ('twobitCModel10', 'twobitCModel', 1024, 8.1318382397263), - ('twobitCModel12', 'twobitCModel', 4096, 7.53026646633342), ('twobitCModel14', 'twobitCModel', 16384, 6.07679338544009), ('twobitCModel16', 'twobitCModel', 65536, 6.07679338544009), - ('gshareCModel6', 'gshareCModel', 64, 10.6602835418646), ('gshareCModel8', 'gshareCModel', 256, 8.38384710559667), ('gshareCModel10', 'gshareCModel', 1024, 6.36847432155534), - ('gshareCModel12', 'gshareCModel', 4096, 3.91108491151983), ('gshareCModel14', 'gshareCModel', 16384, 2.83926519215395), ('gshareCModel16', 'gshareCModel', 65536, .60213659066941)] -RefDataBTB = [('BTBCModel6', 'BTBCModel', 64, 1.11806778745097), ('BTBCModel8', 'BTBCModel', 256, 0.183833943219956), ('BTBCModel10', 'BTBCModel', 1024, 0.0109271020749376), - ('BTBCModel12', 'BTBCModel', 4096, 0.00437600802791213), ('BTBCModel14', 'BTBCModel', 16384, 0.00188756234204305), ('BTBCModel16', 'BTBCModel', 65536, 0.00188756234204305)] + +RefDataBP = [('twobitCModel6', 'twobitCModel', 64, 10.0060297551637), ('twobitCModel8', 'twobitCModel', 256, 8.4320392215602), ('twobitCModel10', 'twobitCModel', 1024, 7.29493318805151), + ('twobitCModel12', 'twobitCModel', 4096, 6.84739616147794), ('twobitCModel14', 'twobitCModel', 16384, 5.68432926870082), ('twobitCModel16', 'twobitCModel', 65536, 5.68432926870082), + ('gshareCModel6', 'gshareCModel', 64, 11.4737703417701), ('gshareCModel8', 'gshareCModel', 256, 8.52341470761974), ('gshareCModel10', 'gshareCModel', 1024, 6.32975690693015), + ('gshareCModel12', 'gshareCModel', 4096, 4.55424632377659), ('gshareCModel14', 'gshareCModel', 16384, 3.54251547725509), ('gshareCModel16', 'gshareCModel', 65536, 1.90424999467293)] +RefDataBTB = [('BTBCModel6', 'BTBCModel', 64, 1.51480272475844), ('BTBCModel8', 'BTBCModel', 256, 0.209057900418965), ('BTBCModel10', 'BTBCModel', 1024, 0.0117345454469572), + ('BTBCModel12', 'BTBCModel', 4096, 0.00125540990359826), ('BTBCModel14', 'BTBCModel', 16384, 0.000732471628510962), ('BTBCModel16', 'BTBCModel', 65536, 0.000732471628510962)] def ParseBranchListFile(path): '''Take the path to the list of Questa Sim log files containing the performance counters outputs. File From 93c356d50dd4953fd3d648df103f9a26209371b9 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Sun, 26 Nov 2023 17:31:23 -0600 Subject: [PATCH 12/48] Added files to ignore file. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f0afc3a98..1664b939f 100644 --- a/.gitignore +++ b/.gitignore @@ -180,3 +180,5 @@ sim/branch*.log benchmarks/embench/wally*.json benchmarks/embench/run* sim/cfi.log +sim/cfi/* +sim/branch/* From 3dfca61c3fbf081d1844fa851dbfebfe4cd224e3 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Sun, 26 Nov 2023 22:19:34 -0600 Subject: [PATCH 13/48] Changes to support concurrent simulation of all the branch predictor sweeps. --- bin/parseHPMC.py | 13 +++++++------ sim/bp-results/branch-list.txt | 24 ++++++++++++------------ sim/bp-results/btb-list.txt | 12 ++++++------ sim/bp-results/class-list.txt | 12 ++++++------ sim/bpred-sim.py | 2 +- 5 files changed, 32 insertions(+), 31 deletions(-) diff --git a/bin/parseHPMC.py b/bin/parseHPMC.py index f9a961c18..d76a52d29 100755 --- a/bin/parseHPMC.py +++ b/bin/parseHPMC.py @@ -276,7 +276,7 @@ def SelectPartition(xlabelListBig, seriesDictBig, group, BenchPerRow): return(xlabelListTrunk, seriesDictTrunk) -def ReportAsGraph(benchmarkDict, bar): +def ReportAsGraph(benchmarkDict, bar, FileName): def FormatToPlot(currBenchmark): names = [] sizes = [] @@ -330,8 +330,8 @@ def ReportAsGraph(benchmarkDict, bar): axes.set_xticks(xdata) axes.set_xticklabels(xdata) axes.grid(color='b', alpha=0.5, linestyle='dashed', linewidth=0.5) - plt.show() - + if(FileName == None): plt.show() + else: plt.savefig(FileName) # if(not args.summary): # size = len(benchmarkDict) @@ -390,7 +390,7 @@ def ReportAsGraph(benchmarkDict, bar): # on each piece. for row in range(0, math.ceil(NumBenchmarks / BenchPerRow)): (xlabelListTrunk, seriesDictTrunk) = SelectPartition(xlabelListBig, seriesDictBig, row, BenchPerRow) - FileName = 'barSegment%d.png' % row + FileName = 'barSegment%d.svg' % row groupLen = len(xlabelListTrunk) BarGraph(seriesDictTrunk, xlabelListTrunk, groupLen, FileName, (row == 0)) @@ -415,7 +415,8 @@ displayMode.add_argument('--text', action='store_const', help='Display in text f displayMode.add_argument('--table', action='store_const', help='Display in text format only.', default=False, const=True) displayMode.add_argument('--gui', action='store_const', help='Display in text format only.', default=False, const=True) displayMode.add_argument('--debug', action='store_const', help='Display in text format only.', default=False, const=True) -parser.add_argument('sources', nargs=1) +parser.add_argument('sources', nargs=1, help='File lists the input Questa transcripts to process.') +parser.add_argument('FileName', metavar='FileName', type=str, nargs='?', help='output graph to file .png If not included outputs to screen.', default=None) args = parser.parse_args() @@ -455,7 +456,7 @@ if(ReportMode == 'text'): ReportAsText(benchmarkDict) if(ReportMode == 'gui'): - ReportAsGraph(benchmarkDict, args.bar) + ReportAsGraph(benchmarkDict, args.bar, args.FileName) # *** this is only needed of -b (no -s) diff --git a/sim/bp-results/branch-list.txt b/sim/bp-results/branch-list.txt index c241610d3..956fc9847 100644 --- a/sim/bp-results/branch-list.txt +++ b/sim/bp-results/branch-list.txt @@ -1,12 +1,12 @@ -gshare6.log gshare 6 -gshare8.log gshare 8 -gshare10.log gshare 10 -gshare12.log gshare 12 -gshare14.log gshare 14 -gshare16.log gshare 16 -twobit6.log twobit 6 -twobit8.log twobit 8 -twobit10.log twobit 10 -twobit12.log twobit 12 -twobit14.log twobit 14 -twobit16.log twobit 16 +../logs/rv32gc_gshare6.log gshare 6 +../logs/rv32gc_gshare8.log gshare 8 +../logs/rv32gc_gshare10.log gshare 10 +../logs/rv32gc_gshare12.log gshare 12 +../logs/rv32gc_gshare14.log gshare 14 +../logs/rv32gc_gshare16.log gshare 16 +../logs/rv32gc_twobit6.log twobit 6 +../logs/rv32gc_twobit8.log twobit 8 +../logs/rv32gc_twobit10.log twobit 10 +../logs/rv32gc_twobit12.log twobit 12 +../logs/rv32gc_twobit14.log twobit 14 +../logs/rv32gc_twobit16.log twobit 16 diff --git a/sim/bp-results/btb-list.txt b/sim/bp-results/btb-list.txt index 741efdf24..30811459e 100644 --- a/sim/bp-results/btb-list.txt +++ b/sim/bp-results/btb-list.txt @@ -1,6 +1,6 @@ -btb6.log btb 6 -btb8.log btb 8 -btb10.log btb 10 -btb12.log btb 12 -btb14.log btb 14 -btb16.log btb 16 +../logs/rv32gc_BTB6.log btb 6 +../logs/rv32gc_BTB8.log btb 8 +../logs/rv32gc_BTB10.log btb 10 +../logs/rv32gc_BTB12.log btb 12 +../logs/rv32gc_BTB14.log btb 14 +../logs/rv32gc_BTB16.log btb 16 diff --git a/sim/bp-results/class-list.txt b/sim/bp-results/class-list.txt index 0d24aa6ee..3926af969 100644 --- a/sim/bp-results/class-list.txt +++ b/sim/bp-results/class-list.txt @@ -1,6 +1,6 @@ -class6.log class 6 -class8.log class 8 -class10.log class 10 -class12.log class 12 -class14.log class 14 -class16.log class 16 +../logs/rv32gc_class6.log class 6 +../logs/rv32gc_class8.log class 8 +../logs/rv32gc_class10.log class 10 +../logs/rv32gc_class12.log class 12 +../logs/rv32gc_class14.log class 14 +../logs/rv32gc_class16.log class 16 diff --git a/sim/bpred-sim.py b/sim/bpred-sim.py index 60af41298..c04b9bd51 100755 --- a/sim/bpred-sim.py +++ b/sim/bpred-sim.py @@ -132,7 +132,7 @@ def main(): # BTB and class size sweep bpdSize = [6, 8, 10, 12, 14, 16] for CurrBPSize in bpdSize: - name = 'BTB'+str(CurrBPSize) + name = 'class'+str(CurrBPSize) configOptions = "+define+INSTR_CLASS_PRED=1 +define+BPRED_OVERRIDE +define+BPRED_TYPE=\`BP_GSHARE" + "+define+BPRED_SIZE=16" + "+define+RAS_SIZE=16+define+BTB_SIZE=" + str(CurrBPSize) + "+define+BTB_OVERRIDE" tc = TestCase( name=name, From 7b0e29c9f7f1af7dffcb48e83cc0efd4b3a81e62 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Sun, 26 Nov 2023 23:13:14 -0600 Subject: [PATCH 14/48] Fixed subtle bug in branch prediction post processing script. --- bin/parseHPMC.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/parseHPMC.py b/bin/parseHPMC.py index d76a52d29..7e8eb7cde 100755 --- a/bin/parseHPMC.py +++ b/bin/parseHPMC.py @@ -255,9 +255,9 @@ def BarGraph(seriesDict, xlabelList, BenchPerRow, FileName, IncludeLegend): fig = plt.subplots(figsize = (EffectiveNumInGroup*BenchPerRow/8, 4)) colors = ['blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'black', 'black', 'black', 'black', 'black', 'black'] for name in seriesDict: - xpos = np.arange(BenchPerRow) - xpos = [x + index*barWidth for x in xpos] values = seriesDict[name] + xpos = np.arange(len(values)) + xpos = [x + index*barWidth for x in xpos] plt.bar(xpos, Inversion(values), width=barWidth, edgecolor='grey', label=name, color=colors[index%len(colors)]) index += 1 plt.xticks([r + barWidth*(NumberInGroup/2-0.5) for r in range(0, BenchPerRow)], xlabelList) From 35a7b2bd24184100a9c74612509c273d6c2cac9f Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Mon, 27 Nov 2023 00:35:22 -0600 Subject: [PATCH 15/48] Last little hickups out of the branch predictor results parsing. --- sim/bp-results/ras-list.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sim/bp-results/ras-list.txt b/sim/bp-results/ras-list.txt index b3e273a3d..c7628ffaa 100644 --- a/sim/bp-results/ras-list.txt +++ b/sim/bp-results/ras-list.txt @@ -1,5 +1,5 @@ -ras3.log ras 3 -ras4.log ras 4 -ras6.log ras 6 -ras10.log ras 10 -ras16.log ras 16 +../logs/rv32gc_RAS3.log ras 3 +../logs/rv32gc_RAS4.log ras 4 +../logs/rv32gc_RAS6.log ras 6 +../logs/rv32gc_RAS10.log ras 10 +../logs/rv32gc_RAS16.log ras 16 From d918791a6078deeb98a1a21eef667bbc05812993 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Mon, 27 Nov 2023 01:26:49 -0600 Subject: [PATCH 16/48] Fixed bug in the wally do script. --- sim/wally-batch.do | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim/wally-batch.do b/sim/wally-batch.do index df34aa0b3..b117e1691 100644 --- a/sim/wally-batch.do +++ b/sim/wally-batch.do @@ -63,10 +63,10 @@ if {$2 eq "buildroot"} { # start and run simulation if { $coverage } { echo "wally-batch buildroot coverage" - vopt wkdir/work_${1}_${2}.testbench -work wkdir/work_${1}_${2} -G RISCV_DIR=$3 -G INSTR_LIMIT=$4 -G INSTR_WAVEON=$5 -G -o testbenchopt +cover=sbecf + vopt wkdir/work_${1}_${2}.testbench -work wkdir/work_${1}_${2} -G RISCV_DIR=$3 -G INSTR_LIMIT=$4 -G INSTR_WAVEON=$5 -o testbenchopt +cover=sbecf vsim -lib wkdir/work_${1}_${2} testbenchopt -suppress 8852,12070,3084,3691,13286 -fatal 7 -cover } else { - vopt wkdir/work_${1}_${2}.testbench -work wkdir/work_${1}_${2} -G RISCV_DIR=$3 -G INSTR_LIMIT=$4 -G INSTR_WAVEON=$5 -G -o testbenchopt + vopt wkdir/work_${1}_${2}.testbench -work wkdir/work_${1}_${2} -G RISCV_DIR=$3 -G INSTR_LIMIT=$4 -G INSTR_WAVEON=$5 -o testbenchopt vsim -lib wkdir/work_${1}_${2} testbenchopt -suppress 8852,12070,3084,3691,13286 -fatal 7 } @@ -76,7 +76,7 @@ if {$2 eq "buildroot"} { } elseif {$2 eq "buildroot-no-trace"} { vlog -lint -work work_${1}_${2} +incdir+../config/$1 +incdir+../config/shared ../testbench/testbench-linux.sv ../testbench/common/*.sv ../src/*/*.sv ../src/*/*/*.sv -suppress 2583 # start and run simulation - vopt +acc work_${1}_${2}.testbench -work work_${1}_${2} -G RISCV_DIR=$3 -G INSTR_LIMIT=$4 -G INSTR_WAVEON=$5 -G -G NO_SPOOFING=1 -o testbenchopt + vopt +acc work_${1}_${2}.testbench -work work_${1}_${2} -G RISCV_DIR=$3 -G INSTR_LIMIT=$4 -G INSTR_WAVEON=$5 -G NO_SPOOFING=1 -o testbenchopt vsim -lib work_${1}_${2} testbenchopt -suppress 8852,12070,3084,3829,13286 -fatal 7 #-- Run the Simulation From d7ef490c125c385130b8f0dab6a22142122e8289 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Mon, 27 Nov 2023 01:27:09 -0600 Subject: [PATCH 17/48] Sutble bug in the cacheway logic for cacheline invalidation. --- src/cache/cacheway.sv | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cache/cacheway.sv b/src/cache/cacheway.sv index 9fb836e93..67f4f5a74 100644 --- a/src/cache/cacheway.sv +++ b/src/cache/cacheway.sv @@ -68,7 +68,7 @@ module cacheway import cvw::*; #(parameter cvw_t P, logic SelTag; logic SelectedWriteWordEn; logic [LINELEN/8-1:0] FinalByteMask; - logic SetValidEN; + logic SetValidEN, ClearValidEN; logic SetValidWay; logic ClearValidWay; logic SetDirtyWay; @@ -102,6 +102,7 @@ module cacheway import cvw::*; #(parameter cvw_t P, assign ClearDirtyWay = ClearDirty & SelData; assign SelectedWriteWordEn = (SetValidWay | SetDirtyWay) & ~FlushStage; // exclusion-tag: icache SelectedWiteWordEn assign SetValidEN = SetValidWay & ~FlushStage; // exclusion-tag: cache SetValidEN + assign ClearValidEN = ClearValidWay & ~FlushStage; // exclusion-tag: cache SetValidEN // If writing the whole line set all write enables to 1, else only set the correct word. assign FinalByteMask = SetValidWay ? '1 : LineByteMask; // OR @@ -156,7 +157,8 @@ module cacheway import cvw::*; #(parameter cvw_t P, if(CacheEn) begin ValidWay <= #1 ValidBits[CacheSet]; if(InvalidateCache) ValidBits <= #1 '0; // exclusion-tag: dcache invalidateway - else if (SetValidEN | (ClearValidWay & ~FlushStage)) ValidBits[CacheSet] <= #1 SetValidWay; + else if (SetValidEN) ValidBits[CacheSet] <= #1 SetValidWay; + else if (ClearValidEN) ValidBits[CacheSet] <= #1 '0; end end From c3da4c3c31fa133567f31e7ca99766728d0fcb7f Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Mon, 27 Nov 2023 12:56:11 -0600 Subject: [PATCH 18/48] Clarified names in cacheway. --- src/cache/cacheway.sv | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cache/cacheway.sv b/src/cache/cacheway.sv index 67f4f5a74..382d9ae6d 100644 --- a/src/cache/cacheway.sv +++ b/src/cache/cacheway.sv @@ -65,7 +65,7 @@ module cacheway import cvw::*; #(parameter cvw_t P, logic [LINELEN-1:0] ReadDataLine; logic [TAGLEN-1:0] ReadTag; logic Dirty; - logic SelTag; + logic SelDirty; logic SelectedWriteWordEn; logic [LINELEN/8-1:0] FinalByteMask; logic SetValidEN, ClearValidEN; @@ -78,7 +78,7 @@ module cacheway import cvw::*; #(parameter cvw_t P, if (!READ_ONLY_CACHE) begin:flushlogic logic FlushWayEn; - mux2 #(1) seltagmux(VictimWay, FlushWay, SelFlush, SelTag); + mux2 #(1) seltagmux(VictimWay, FlushWay, SelFlush, SelDirty); // FlushWay is part of a one hot way selection. Must clear it if FlushWay not selected. // coverage off -item e 1 -fecexprrow 3 @@ -86,11 +86,11 @@ module cacheway import cvw::*; #(parameter cvw_t P, assign FlushWayEn = FlushWay & SelFlush; assign SelNonHit = FlushWayEn | SelWay; end else begin:flushlogic // no flush operation for read-only caches. - assign SelTag = VictimWay; + assign SelDirty = VictimWay; assign SelNonHit = SelWay; end - mux2 #(1) selectedwaymux(HitWay, SelTag, SelNonHit , SelData); + mux2 #(1) selectedwaymux(HitWay, SelDirty, SelNonHit , SelData); ///////////////////////////////////////////////////////////////////////////////////////////// // Write Enable demux @@ -117,7 +117,7 @@ module cacheway import cvw::*; #(parameter cvw_t P, // AND portion of distributed tag multiplexer assign TagWay = SelData ? ReadTag : '0; // AND part of AOMux - assign DirtyWay = SelTag & Dirty & ValidWay; + assign DirtyWay = SelDirty & Dirty & ValidWay; assign HitWay = ValidWay & (ReadTag == PAdr[PA_BITS-1:OFFSETLEN+INDEXLEN]); ///////////////////////////////////////////////////////////////////////////////////////////// From 08549446ef576c5bee9143a1ef18a2bb7a01e49b Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Mon, 27 Nov 2023 13:13:36 -0600 Subject: [PATCH 19/48] Reduced cache fsm complexity. --- src/cache/cachefsm.sv | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cache/cachefsm.sv b/src/cache/cachefsm.sv index 8a11dd735..0e07793d6 100644 --- a/src/cache/cachefsm.sv +++ b/src/cache/cachefsm.sv @@ -130,8 +130,7 @@ module cachefsm import cvw::*; #(parameter cvw_t P, STATE_READ_HOLD: if(Stall) NextState = STATE_READ_HOLD; else NextState = STATE_READY; // exclusion-tag-start: icache case - STATE_WRITEBACK: if (CacheBusAck & (CMOp[1] | CMOp[2])) NextState = STATE_READ_HOLD; - else if(CacheBusAck & ~CMOp[3]) NextState = STATE_FETCH; + STATE_WRITEBACK: if(CacheBusAck & ~CMOp[3]) NextState = STATE_FETCH; else if(CacheBusAck) NextState = STATE_READ_HOLD; else NextState = STATE_WRITEBACK; // eviction needs a delay as the bus fsm does not correctly handle sending the write command at the same time as getting back the bus ack. From 337903d8dd7019f7201e5e4c615bc914015990fc Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Mon, 27 Nov 2023 14:59:42 -0600 Subject: [PATCH 20/48] More cache simplifications. --- src/cache/cache.sv | 13 +++---------- src/cache/cachefsm.sv | 3 --- src/lsu/lsu.sv | 13 ++++++++++--- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/cache/cache.sv b/src/cache/cache.sv index 4701fc4c7..c527f0eae 100644 --- a/src/cache/cache.sv +++ b/src/cache/cache.sv @@ -98,8 +98,6 @@ module cache import cvw::*; #(parameter cvw_t P, logic SelWay; logic [LINELEN/8-1:0] LineByteMask; logic [$clog2(LINELEN/8) - $clog2(MUXINTERVAL/8) - 1:0] WordOffsetAddr; - logic ZeroCacheLine; - logic [LINELEN-1:0] PreLineWriteData; genvar index; ///////////////////////////////////////////////////////////////////////////////////////////// @@ -161,11 +159,6 @@ module cache import cvw::*; #(parameter cvw_t P, ///////////////////////////////////////////////////////////////////////////////////////////// // Write Path ///////////////////////////////////////////////////////////////////////////////////////////// - if(P.ZICBOZ_SUPPORTED) begin : cboz_supported - mux2 #(LINELEN) WriteDataMux(FetchBuffer, '0, ZeroCacheLine, PreLineWriteData); - end else begin - assign PreLineWriteData = FetchBuffer; - end if(!READ_ONLY_CACHE) begin:WriteSelLogic logic [LINELEN/8-1:0] DemuxedByteMask, FetchBufferByteSel; @@ -185,14 +178,14 @@ module cache import cvw::*; #(parameter cvw_t P, // Merge write data into fetched cache line for store miss for(index = 0; index < LINELEN/8; index++) begin mux2 #(8) WriteDataMux(.d0(CacheWriteData[(8*index)%WORDLEN+7:(8*index)%WORDLEN]), - .d1(PreLineWriteData[8*index+7:8*index]), .s(FetchBufferByteSel[index] | ZeroCacheLine), .y(LineWriteData[8*index+7:8*index])); + .d1(FetchBuffer[8*index+7:8*index]), .s(FetchBufferByteSel[index] & ~CMOp[3]), .y(LineWriteData[8*index+7:8*index])); end assign LineByteMask = SetValid ? '1 : SetDirty ? DemuxedByteMask : '0; end else begin:WriteSelLogic // No need for this mux if the cache does not handle writes. - assign LineWriteData = PreLineWriteData; + assign LineWriteData = FetchBuffer; assign LineByteMask = '1; end @@ -227,7 +220,7 @@ module cache import cvw::*; #(parameter cvw_t P, .FlushStage, .CacheRW, .Stall, .CacheHit, .LineDirty, .CacheStall, .CacheCommitted, .CacheMiss, .CacheAccess, .SelAdr, .SelWay, - .ClearDirty, .SetDirty, .SetValid, .ClearValid, .ZeroCacheLine, .SelWriteback, .SelFlush, + .ClearDirty, .SetDirty, .SetValid, .ClearValid, .SelWriteback, .SelFlush, .FlushAdrCntEn, .FlushWayCntEn, .FlushCntRst, .FlushAdrFlag, .FlushWayFlag, .FlushCache, .SelFetchBuffer, .InvalidateCache, .CMOp, .CacheEn, .LRUWriteEn); diff --git a/src/cache/cachefsm.sv b/src/cache/cachefsm.sv index 0e07793d6..e7e5e0306 100644 --- a/src/cache/cachefsm.sv +++ b/src/cache/cachefsm.sv @@ -58,7 +58,6 @@ module cachefsm import cvw::*; #(parameter cvw_t P, output logic ClearValid, // Clear the valid bit in the selected way and set output logic SetDirty, // Set the dirty bit in the selected way and set output logic ClearDirty, // Clear the dirty bit in the selected way and set - output logic ZeroCacheLine, // Write zeros to all bytes of cacheline output logic SelWriteback, // Overrides cached tag check to select a specific way and set for writeback output logic LRUWriteEn, // Update the LRU state output logic SelFlush, // [0] Use SelAdr, [1] SRAM reads/writes from FlushAdr @@ -174,8 +173,6 @@ module cachefsm import cvw::*; #(parameter cvw_t P, assign SelWay = (CurrState == STATE_WRITEBACK & ((~CacheBusAck & ~(CMOp[1] | CMOp[2])) | (P.ZICBOZ_SUPPORTED & CacheBusAck & CMOp[3]))) | (CurrState == STATE_READY & ((AnyMiss & LineDirty) | (P.ZICBOZ_SUPPORTED & CMOZeroNoEviction & ~CacheHit))) | (CurrState == STATE_WRITE_LINE); - assign ZeroCacheLine = P.ZICBOZ_SUPPORTED & ((CurrState == STATE_READY & CMOZeroNoEviction) | - (CurrState == STATE_WRITEBACK & (CMOp[3] & CacheBusAck))); assign SelWriteback = (CurrState == STATE_WRITEBACK & (CMOp[1] | CMOp[2] | ~CacheBusAck)) | (CurrState == STATE_READY & AnyMiss & LineDirty); assign SelFlush = (CurrState == STATE_READY & FlushCache) | diff --git a/src/lsu/lsu.sv b/src/lsu/lsu.sv index 5c21d7ecd..d82c9c02d 100644 --- a/src/lsu/lsu.sv +++ b/src/lsu/lsu.sv @@ -148,7 +148,8 @@ module lsu import cvw::*; #(parameter cvw_t P) ( logic IgnoreRequestTLB; // On either ITLB or DTLB miss, ignore miss so HPTW can handle logic IgnoreRequest; // On FlushM or TLB miss ignore memory operation logic SelDTIM; // Select DTIM rather than bus or D$ - + logic [P.XLEN-1:0] WriteDataZM; + ///////////////////////////////////////////////////////////////////////////////////////////// // Pipeline for IEUAdr E to M // Zero-extend address to 34 bits for XLEN=32 @@ -176,6 +177,12 @@ module lsu import cvw::*; #(parameter cvw_t P) ( assign {SpillStallM, SelStoreDelay} = '0; end + if(P.ZICBOZ_SUPPORTED) begin : cboz + mux2 #(P.XLEN) writedatacbozmux(WriteDataM, '0, CMOpM[3], WriteDataZM); + end else begin : cboz + assign WriteDataZM = WriteDataM; + end + ///////////////////////////////////////////////////////////////////////////////////////////// // HPTW (only needed if VM supported) // MMU include PMP and is needed if any privileged supported @@ -187,7 +194,7 @@ module lsu import cvw::*; #(parameter cvw_t P) ( .FlushW, .DCacheStallM, .SATP_REGW, .PCSpillF, .STATUS_MXR, .STATUS_SUM, .STATUS_MPRV, .STATUS_MPP, .ENVCFG_HADE, .PrivilegeModeW, .ReadDataM(ReadDataM[P.XLEN-1:0]), // ReadDataM is LLEN, but HPTW only needs XLEN - .WriteDataM, .Funct3M, .LSUFunct3M, .Funct7M, .LSUFunct7M, + .WriteDataM(WriteDataZM), .Funct3M, .LSUFunct3M, .Funct7M, .LSUFunct7M, .IEUAdrExtM, .PTE, .IHWriteDataM, .PageType, .PreLSURWM, .LSUAtomicM, .IHAdrM, .HPTWStall, .SelHPTW, .IgnoreRequestTLB, .LSULoadAccessFaultM, .LSUStoreAmoAccessFaultM, @@ -198,7 +205,7 @@ module lsu import cvw::*; #(parameter cvw_t P) ( assign LSUFunct3M = Funct3M; assign LSUFunct7M = Funct7M; assign LSUAtomicM = AtomicM; - assign IHWriteDataM = WriteDataM; + assign IHWriteDataM = WriteDataZM; assign LoadAccessFaultM = LSULoadAccessFaultM; assign StoreAmoAccessFaultM = LSUStoreAmoAccessFaultM; assign {HPTWStall, SelHPTW, PTE, PageType, DTLBWriteM, ITLBWriteF, IgnoreRequestTLB} = '0; From beb95dd592431023408113658263a6613c9b3333 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Mon, 27 Nov 2023 17:44:11 -0600 Subject: [PATCH 21/48] Modified the pmachecker to correctly check the permissions for cmo instructions. However this isn't fully tested. --- src/ifu/ifu.sv | 2 +- src/lsu/lsu.sv | 12 ++++++++---- src/mmu/adrdecs.sv | 12 ++++++------ src/mmu/mmu.sv | 3 ++- src/mmu/pmachecker.sv | 11 +++++++---- src/uncore/uncore.sv | 2 +- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/ifu/ifu.sv b/src/ifu/ifu.sv index 831d9e6bb..7d61bd4db 100644 --- a/src/ifu/ifu.sv +++ b/src/ifu/ifu.sv @@ -182,7 +182,7 @@ module ifu import cvw::*; #(parameter cvw_t P) ( .InstrAccessFaultF, .LoadAccessFaultM(), .StoreAmoAccessFaultM(), .InstrPageFaultF, .LoadPageFaultM(), .StoreAmoPageFaultM(), .LoadMisalignedFaultM(), .StoreAmoMisalignedFaultM(), - .UpdateDA(InstrUpdateDAF), + .UpdateDA(InstrUpdateDAF), .CMOp(4'b0), .AtomicAccessM(1'b0),.ExecuteAccessF(1'b1), .WriteAccessM(1'b0), .ReadAccessM(1'b0), .PMPCFG_ARRAY_REGW, .PMPADDR_ARRAY_REGW); diff --git a/src/lsu/lsu.sv b/src/lsu/lsu.sv index d82c9c02d..2bb604d39 100644 --- a/src/lsu/lsu.sv +++ b/src/lsu/lsu.sv @@ -228,7 +228,7 @@ module lsu import cvw::*; #(parameter cvw_t P) ( logic DisableTranslation; // During HPTW walk or D$ flush disable virtual memory address translation logic WriteAccessM; assign DisableTranslation = SelHPTW | FlushDCacheM; - assign WriteAccessM = PreLSURWM[0] | (|CMOpM); + assign WriteAccessM = PreLSURWM[0]; mmu #(.P(P), .TLB_ENTRIES(P.DTLB_ENTRIES), .IMMU(0)) dmmu(.clk, .reset, .SATP_REGW, .STATUS_MXR, .STATUS_SUM, .STATUS_MPRV, .STATUS_MPP, .ENVCFG_PBMTE, .ENVCFG_HADE, .PrivilegeModeW, .DisableTranslation, .VAdr(IHAdrM), .Size(LSUFunct3M[1:0]), @@ -238,7 +238,7 @@ module lsu import cvw::*; #(parameter cvw_t P) ( .StoreAmoAccessFaultM(LSUStoreAmoAccessFaultM), .InstrPageFaultF(), .LoadPageFaultM, .StoreAmoPageFaultM, .LoadMisalignedFaultM, .StoreAmoMisalignedFaultM, // *** these faults need to be supressed during hptw. - .UpdateDA(DataUpdateDAM), + .UpdateDA(DataUpdateDAM), .CMOp(CMOpM), .AtomicAccessM(|LSUAtomicM), .ExecuteAccessF(1'b0), .WriteAccessM, .ReadAccessM(PreLSURWM[1]), .PMPCFG_ARRAY_REGW, .PMPADDR_ARRAY_REGW); @@ -301,8 +301,12 @@ module lsu import cvw::*; #(parameter cvw_t P) ( logic FlushDCache; // Suppress d cache flush if there is an ITLB miss. logic CacheStall; logic [1:0] CacheBusRWTemp; - - assign BusRW = ~CacheableM & ~SelDTIM ? LSURWM : '0; + + if(P.ZICBOZ_SUPPORTED) begin + assign BusRW = ~CacheableM & ~SelDTIM ? CMOpM[3] ? 2'b01 : LSURWM : '0; + end else begin + assign BusRW = ~CacheableM & ~SelDTIM ? LSURWM : '0; + end assign CacheableOrFlushCacheM = CacheableM | FlushDCacheM; assign CacheRWM = CacheableM & ~SelDTIM ? LSURWM : '0; assign FlushDCache = FlushDCacheM & ~(SelHPTW); diff --git a/src/mmu/adrdecs.sv b/src/mmu/adrdecs.sv index 3ee9c23d5..69b3a8c1d 100644 --- a/src/mmu/adrdecs.sv +++ b/src/mmu/adrdecs.sv @@ -30,18 +30,18 @@ module adrdecs import cvw::*; #(parameter cvw_t P) ( input logic [P.PA_BITS-1:0] PhysicalAddress, - input logic AccessRW, AccessRX, AccessRWX, + input logic AccessRW, AccessRX, AccessRWXZ, AccessRWZ, AccessRXZ, input logic [1:0] Size, output logic [11:0] SelRegions ); localparam logic [3:0] SUPPORTED_SIZE = (P.LLEN == 32 ? 4'b0111 : 4'b1111); // Determine which region of physical memory (if any) is being accessed - adrdec #(P.PA_BITS) dtimdec(PhysicalAddress, P.DTIM_BASE[P.PA_BITS-1:0], P.DTIM_RANGE[P.PA_BITS-1:0], P.DTIM_SUPPORTED, AccessRW, Size, SUPPORTED_SIZE, SelRegions[11]); - adrdec #(P.PA_BITS) iromdec(PhysicalAddress, P.IROM_BASE[P.PA_BITS-1:0], P.IROM_RANGE[P.PA_BITS-1:0], P.IROM_SUPPORTED, AccessRX, Size, SUPPORTED_SIZE, SelRegions[10]); - adrdec #(P.PA_BITS) ddr4dec(PhysicalAddress, P.EXT_MEM_BASE[P.PA_BITS-1:0], P.EXT_MEM_RANGE[P.PA_BITS-1:0], P.EXT_MEM_SUPPORTED, AccessRWX, Size, SUPPORTED_SIZE, SelRegions[9]); - adrdec #(P.PA_BITS) bootromdec(PhysicalAddress, P.BOOTROM_BASE[P.PA_BITS-1:0], P.BOOTROM_RANGE[P.PA_BITS-1:0], P.BOOTROM_SUPPORTED, AccessRX, Size, SUPPORTED_SIZE, SelRegions[8]); - adrdec #(P.PA_BITS) uncoreramdec(PhysicalAddress, P.UNCORE_RAM_BASE[P.PA_BITS-1:0], P.UNCORE_RAM_RANGE[P.PA_BITS-1:0], P.UNCORE_RAM_SUPPORTED, AccessRWX, Size, SUPPORTED_SIZE, SelRegions[7]); + adrdec #(P.PA_BITS) dtimdec(PhysicalAddress, P.DTIM_BASE[P.PA_BITS-1:0], P.DTIM_RANGE[P.PA_BITS-1:0], P.DTIM_SUPPORTED, AccessRWZ, Size, SUPPORTED_SIZE, SelRegions[11]); + adrdec #(P.PA_BITS) iromdec(PhysicalAddress, P.IROM_BASE[P.PA_BITS-1:0], P.IROM_RANGE[P.PA_BITS-1:0], P.IROM_SUPPORTED, AccessRXZ, Size, SUPPORTED_SIZE, SelRegions[10]); + adrdec #(P.PA_BITS) ddr4dec(PhysicalAddress, P.EXT_MEM_BASE[P.PA_BITS-1:0], P.EXT_MEM_RANGE[P.PA_BITS-1:0], P.EXT_MEM_SUPPORTED, AccessRWXZ, Size, SUPPORTED_SIZE, SelRegions[9]); + adrdec #(P.PA_BITS) bootromdec(PhysicalAddress, P.BOOTROM_BASE[P.PA_BITS-1:0], P.BOOTROM_RANGE[P.PA_BITS-1:0], P.BOOTROM_SUPPORTED, AccessRXZ, Size, SUPPORTED_SIZE, SelRegions[8]); + adrdec #(P.PA_BITS) uncoreramdec(PhysicalAddress, P.UNCORE_RAM_BASE[P.PA_BITS-1:0], P.UNCORE_RAM_RANGE[P.PA_BITS-1:0], P.UNCORE_RAM_SUPPORTED, AccessRWXZ, Size, SUPPORTED_SIZE, SelRegions[7]); adrdec #(P.PA_BITS) clintdec(PhysicalAddress, P.CLINT_BASE[P.PA_BITS-1:0], P.CLINT_RANGE[P.PA_BITS-1:0], P.CLINT_SUPPORTED, AccessRW, Size, SUPPORTED_SIZE, SelRegions[6]); adrdec #(P.PA_BITS) gpiodec(PhysicalAddress, P.GPIO_BASE[P.PA_BITS-1:0], P.GPIO_RANGE[P.PA_BITS-1:0], P.GPIO_SUPPORTED, AccessRW, Size, 4'b0100, SelRegions[5]); adrdec #(P.PA_BITS) uartdec(PhysicalAddress, P.UART_BASE[P.PA_BITS-1:0], P.UART_RANGE[P.PA_BITS-1:0], P.UART_SUPPORTED, AccessRW, Size, 4'b0001, SelRegions[4]); diff --git a/src/mmu/mmu.sv b/src/mmu/mmu.sv index a497b6da7..16016ac47 100644 --- a/src/mmu/mmu.sv +++ b/src/mmu/mmu.sv @@ -55,6 +55,7 @@ module mmu import cvw::*; #(parameter cvw_t P, output logic UpdateDA, // page fault due to setting dirty or access bit output logic LoadMisalignedFaultM, StoreAmoMisalignedFaultM, // misaligned fault sources // PMA checker signals + input logic [3:0] CMOp, // Cache management instructions input logic AtomicAccessM, ExecuteAccessF, WriteAccessM, ReadAccessM, // access type input var logic [7:0] PMPCFG_ARRAY_REGW[P.PMP_ENTRIES-1:0], // PMP configuration input var logic [P.PA_BITS-3:0] PMPADDR_ARRAY_REGW[P.PMP_ENTRIES-1:0] // PMP addresses @@ -106,7 +107,7 @@ module mmu import cvw::*; #(parameter cvw_t P, // Check physical memory accesses /////////////////////////////////////////// - pmachecker #(P) pmachecker(.PhysicalAddress, .Size, + pmachecker #(P) pmachecker(.PhysicalAddress, .Size, .CMOp, .AtomicAccessM, .ExecuteAccessF, .WriteAccessM, .ReadAccessM, .PBMemoryType, .Cacheable, .Idempotent, .SelTIM, .PMAInstrAccessFaultF, .PMALoadAccessFaultM, .PMAStoreAmoAccessFaultM); diff --git a/src/mmu/pmachecker.sv b/src/mmu/pmachecker.sv index 7aa20fc2f..1ccf6501c 100644 --- a/src/mmu/pmachecker.sv +++ b/src/mmu/pmachecker.sv @@ -31,6 +31,7 @@ module pmachecker import cvw::*; #(parameter cvw_t P) ( input logic [P.PA_BITS-1:0] PhysicalAddress, input logic [1:0] Size, + input logic [3:0] CMOp, input logic AtomicAccessM, // Atomic access input logic ExecuteAccessF, // Execute access input logic WriteAccessM, // Write access @@ -43,18 +44,20 @@ module pmachecker import cvw::*; #(parameter cvw_t P) ( ); logic PMAAccessFault; - logic AccessRW, AccessRWX, AccessRX; + logic AccessRW, AccessRWXZ, AccessRX, AccessRWZ, AccessRXZ; logic [11:0] SelRegions; logic AtomicAllowed; logic CacheableRegion, IdempotentRegion; // Determine what type of access is being made assign AccessRW = ReadAccessM | WriteAccessM; - assign AccessRWX = ReadAccessM | WriteAccessM | ExecuteAccessF; + assign AccessRWZ = AccessRW | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])); + assign AccessRWXZ = ReadAccessM | WriteAccessM | ExecuteAccessF | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])) | (P.ZICBOZ_SUPPORTED & (CMOp[3])); assign AccessRX = ReadAccessM | ExecuteAccessF; + assign AccessRXZ = AccessRX | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])); // Determine which region of physical memory (if any) is being accessed - adrdecs #(P) adrdecs(PhysicalAddress, AccessRW, AccessRX, AccessRWX, Size, SelRegions); + adrdecs #(P) adrdecs(PhysicalAddress, AccessRW, AccessRX, AccessRWXZ, AccessRWZ, AccessRXZ, Size, SelRegions); // Only non-core RAM/ROM memory regions are cacheable. PBMT can override cachable; NC and IO are uncachable assign CacheableRegion = SelRegions[9] | SelRegions[8] | SelRegions[7]; // exclusion-tag: unused-cachable @@ -71,7 +74,7 @@ module pmachecker import cvw::*; #(parameter cvw_t P) ( assign SelTIM = SelRegions[11] | SelRegions[10]; // exclusion-tag: unused-idempotent // Detect access faults - assign PMAAccessFault = (SelRegions[0]) & AccessRWX | AtomicAccessM & ~AtomicAllowed; + assign PMAAccessFault = (SelRegions[0]) & AccessRWXZ | AtomicAccessM & ~AtomicAllowed; assign PMAInstrAccessFaultF = ExecuteAccessF & PMAAccessFault; assign PMALoadAccessFaultM = ReadAccessM & PMAAccessFault; assign PMAStoreAmoAccessFaultM = WriteAccessM & PMAAccessFault; diff --git a/src/uncore/uncore.sv b/src/uncore/uncore.sv index 60d197f78..ee93c2904 100644 --- a/src/uncore/uncore.sv +++ b/src/uncore/uncore.sv @@ -88,7 +88,7 @@ module uncore import cvw::*; #(parameter cvw_t P)( // Determine which region of physical memory (if any) is being accessed // Use a trimmed down portion of the PMA checker - only the address decoders // Set access types to all 1 as don't cares because the MMU has already done access checking - adrdecs #(P) adrdecs(HADDR, 1'b1, 1'b1, 1'b1, HSIZE[1:0], HSELRegions); + adrdecs #(P) adrdecs(HADDR, 1'b1, 1'b1, 1'b1, 1'b1, 1'b1, HSIZE[1:0], HSELRegions); // unswizzle HSEL signals assign {HSELDTIM, HSELIROM, HSELEXT, HSELBootRom, HSELRam, HSELCLINT, HSELGPIO, HSELUART, HSELPLIC, HSELEXTSDC, HSELSPI} = HSELRegions[11:1]; From 9290c3f95798df94d01bce762c41802307ca482a Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Mon, 27 Nov 2023 20:57:33 -0600 Subject: [PATCH 22/48] Added correct cbo fault behavior. --- src/mmu/pmachecker.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mmu/pmachecker.sv b/src/mmu/pmachecker.sv index 1ccf6501c..a48ccd241 100644 --- a/src/mmu/pmachecker.sv +++ b/src/mmu/pmachecker.sv @@ -77,5 +77,5 @@ module pmachecker import cvw::*; #(parameter cvw_t P) ( assign PMAAccessFault = (SelRegions[0]) & AccessRWXZ | AtomicAccessM & ~AtomicAllowed; assign PMAInstrAccessFaultF = ExecuteAccessF & PMAAccessFault; assign PMALoadAccessFaultM = ReadAccessM & PMAAccessFault; - assign PMAStoreAmoAccessFaultM = WriteAccessM & PMAAccessFault; + assign PMAStoreAmoAccessFaultM = (WriteAccessM | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])) | (P.ZICBOZ_SUPPORTED & CMOp[3])) & PMAAccessFault; endmodule From 195def58080d68602520b8eab28e301776397390 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Mon, 27 Nov 2023 21:24:30 -0600 Subject: [PATCH 23/48] Extended the abhcacheinterface to zero a cacheline's worth of uncached memory on cbo.zero. --- src/ebu/ahbcacheinterface.sv | 9 ++++++--- src/ebu/buscachefsm.sv | 16 ++++++++++------ src/ifu/ifu.sv | 2 +- src/lsu/lsu.sv | 8 +++++--- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/ebu/ahbcacheinterface.sv b/src/ebu/ahbcacheinterface.sv index 054022106..78b0d15e8 100644 --- a/src/ebu/ahbcacheinterface.sv +++ b/src/ebu/ahbcacheinterface.sv @@ -66,6 +66,7 @@ module ahbcacheinterface #( input logic [LLEN-1:0] WriteDataM, // IEU write data for uncached store input logic [1:0] BusRW, // Uncached memory operation read/write control: 10: read, 01: write input logic [2:0] Funct3, // Size of uncached memory operation + input logic BusCMOZero, // Uncached cbo.zero must write zero to full sized cacheline without going through the cache // lsu/ifu interface input logic Stall, // Core pipeline is stalled @@ -80,6 +81,7 @@ module ahbcacheinterface #( logic CaptureEn; // Enable updating the Fetch buffer with valid data from HRDATA logic [AHBW/8-1:0] BusByteMaskM; // Byte enables within a word. For cache request all 1s logic [AHBW-1:0] PreHWDATA; // AHB Address phase write data + logic [PA_BITS-1:0] PAdrZero; genvar index; @@ -91,10 +93,11 @@ module ahbcacheinterface #( .q(FetchBuffer[(index+1)*AHBW-1:index*AHBW])); end - mux2 #(PA_BITS) localadrmux(PAdr, CacheBusAdr, Cacheable, LocalHADDR); + assign PAdrZero = BusCMOZero ? {PAdr[PA_BITS-1:$clog2(LINELEN/8)], {$clog2(LINELEN/8){1'b0}}} : PAdr; + mux2 #(PA_BITS) localadrmux(PAdrZero, CacheBusAdr, Cacheable, LocalHADDR); assign HADDR = ({{PA_BITS-AHBWLOGBWPL{1'b0}}, BeatCount} << $clog2(AHBW/8)) + LocalHADDR; - mux2 #(3) sizemux(.d0(Funct3), .d1(AHBW == 32 ? 3'b010 : 3'b011), .s(Cacheable), .y(HSIZE)); + mux2 #(3) sizemux(.d0(Funct3), .d1(AHBW == 32 ? 3'b010 : 3'b011), .s(Cacheable | BusCMOZero), .y(HSIZE)); // When AHBW is less than LLEN need extra muxes to select the subword from cache's read data. logic [AHBW-1:0] CacheReadDataWordAHB; @@ -119,6 +122,6 @@ module ahbcacheinterface #( buscachefsm #(BeatCountThreshold, AHBWLOGBWPL, READ_ONLY_CACHE) AHBBuscachefsm( .HCLK, .HRESETn, .Flush, .BusRW, .Stall, .BusCommitted, .BusStall, .CaptureEn, .SelBusBeat, - .CacheBusRW, .CacheBusAck, .BeatCount, .BeatCountDelayed, + .CacheBusRW, .BusCMOZero, .CacheBusAck, .BeatCount, .BeatCountDelayed, .HREADY, .HTRANS, .HWRITE, .HBURST); endmodule diff --git a/src/ebu/buscachefsm.sv b/src/ebu/buscachefsm.sv index 4d1d475d8..45f66762f 100644 --- a/src/ebu/buscachefsm.sv +++ b/src/ebu/buscachefsm.sv @@ -42,6 +42,7 @@ module buscachefsm #( input logic Stall, // Core pipeline is stalled input logic Flush, // Pipeline stage flush. Prevents bus transaction from starting input logic [1:0] BusRW, // Uncached memory operation read/write control: 10: read, 01: write + input logic BusCMOZero, // Uncached cbo.zero must write zero to full sized cacheline without going through the cache output logic BusStall, // Bus is busy with an in flight memory operation output logic BusCommitted, // Bus is busy with an in flight memory operation and it is not safe to take an interrupt @@ -75,6 +76,9 @@ module buscachefsm #( logic BeatCntEn; logic BeatCntReset; logic CacheAccess; + logic BusWrite; + + assign BusWrite = CacheBusRW[0] | BusCMOZero; always_ff @(posedge HCLK) if (~HRESETn | Flush) CurrState <= #1 ADR_PHASE; @@ -83,18 +87,18 @@ module buscachefsm #( always_comb begin case(CurrState) ADR_PHASE: if (HREADY & |BusRW) NextState = DATA_PHASE; - else if (HREADY & CacheBusRW[0]) NextState = CACHE_WRITEBACK; + else if (HREADY & BusWrite) NextState = CACHE_WRITEBACK; else if (HREADY & CacheBusRW[1]) NextState = CACHE_FETCH; else NextState = ADR_PHASE; DATA_PHASE: if(HREADY) NextState = MEM3; else NextState = DATA_PHASE; MEM3: if(Stall) NextState = MEM3; else NextState = ADR_PHASE; - CACHE_FETCH: if(HREADY & FinalBeatCount & CacheBusRW[0]) NextState = CACHE_WRITEBACK; + CACHE_FETCH: if(HREADY & FinalBeatCount & BusWrite) NextState = CACHE_WRITEBACK; else if(HREADY & FinalBeatCount & CacheBusRW[1]) NextState = CACHE_FETCH; else if(HREADY & FinalBeatCount & ~|CacheBusRW) NextState = ADR_PHASE; else NextState = CACHE_FETCH; - CACHE_WRITEBACK: if(HREADY & FinalBeatCount & CacheBusRW[0]) NextState = CACHE_WRITEBACK; + CACHE_WRITEBACK: if(HREADY & FinalBeatCount & BusWrite) NextState = CACHE_WRITEBACK; else if(HREADY & FinalBeatCount & CacheBusRW[1]) NextState = CACHE_FETCH; else if(HREADY & FinalBeatCount & ~|CacheBusRW) NextState = ADR_PHASE; else NextState = CACHE_WRITEBACK; @@ -128,7 +132,7 @@ module buscachefsm #( (CacheAccess & FinalBeatCount & |CacheBusRW & HREADY & ~Flush) ? AHB_NONSEQ : // if we have a pipelined request (CacheAccess & |BeatCount) ? (`BURST_EN ? AHB_SEQ : AHB_NONSEQ) : AHB_IDLE; - assign HWRITE = (BusRW[0] | CacheBusRW[0] & ~Flush) | (CurrState == CACHE_WRITEBACK & |BeatCount); + assign HWRITE = (BusRW[0] | BusWrite & ~Flush) | (CurrState == CACHE_WRITEBACK & |BeatCount); assign HBURST = `BURST_EN & ((|CacheBusRW & ~Flush) | (CacheAccess & |BeatCount)) ? LocalBurstType : 3'b0; always_comb begin @@ -142,8 +146,8 @@ module buscachefsm #( end // communication to cache - assign CacheBusAck = (CacheAccess & HREADY & FinalBeatCount); - assign SelBusBeat = (CurrState == ADR_PHASE & (BusRW[0] | CacheBusRW[0])) | + assign CacheBusAck = (CacheAccess & HREADY & FinalBeatCount & ~BusCMOZero); + assign SelBusBeat = (CurrState == ADR_PHASE & (BusRW[0] | BusWrite)) | (CurrState == DATA_PHASE & BusRW[0]) | (CurrState == CACHE_WRITEBACK) | (CurrState == CACHE_FETCH); diff --git a/src/ifu/ifu.sv b/src/ifu/ifu.sv index 7d61bd4db..4a02848b5 100644 --- a/src/ifu/ifu.sv +++ b/src/ifu/ifu.sv @@ -252,7 +252,7 @@ module ifu import cvw::*; #(parameter cvw_t P) ( ahbcacheinterface #(P.AHBW, P.LLEN, P.PA_BITS, WORDSPERLINE, LOGBWPL, LINELEN, LLENPOVERAHBW, 1) ahbcacheinterface(.HCLK(clk), .HRESETn(~reset), .HRDATA, - .Flush(FlushD), .CacheBusRW, .HSIZE(IFUHSIZE), .HBURST(IFUHBURST), .HTRANS(IFUHTRANS), .HWSTRB(), + .Flush(FlushD), .CacheBusRW, .BusCMOZero(1'b0), .HSIZE(IFUHSIZE), .HBURST(IFUHBURST), .HTRANS(IFUHTRANS), .HWSTRB(), .Funct3(3'b010), .HADDR(IFUHADDR), .HREADY(IFUHREADY), .HWRITE(IFUHWRITE), .CacheBusAdr(ICacheBusAdr), .BeatCount(), .Cacheable(CacheableF), .SelBusBeat(), .WriteDataM('0), .CacheBusAck(ICacheBusAck), .HWDATA(), .CacheableOrFlushCacheM(1'b0), .CacheReadDataWordM('0), diff --git a/src/lsu/lsu.sv b/src/lsu/lsu.sv index 2bb604d39..6fe4377fc 100644 --- a/src/lsu/lsu.sv +++ b/src/lsu/lsu.sv @@ -301,12 +301,14 @@ module lsu import cvw::*; #(parameter cvw_t P) ( logic FlushDCache; // Suppress d cache flush if there is an ITLB miss. logic CacheStall; logic [1:0] CacheBusRWTemp; + logic BusCMOZero; if(P.ZICBOZ_SUPPORTED) begin - assign BusRW = ~CacheableM & ~SelDTIM ? CMOpM[3] ? 2'b01 : LSURWM : '0; + assign BusCMOZero = CMOpM[3] & ~CacheableM; end else begin - assign BusRW = ~CacheableM & ~SelDTIM ? LSURWM : '0; + assign BusCMOZero = '0; end + assign BusRW = ~CacheableM & ~SelDTIM ? LSURWM : '0; assign CacheableOrFlushCacheM = CacheableM | FlushDCacheM; assign CacheRWM = CacheableM & ~SelDTIM ? LSURWM : '0; assign FlushDCache = FlushDCacheM & ~(SelHPTW); @@ -332,7 +334,7 @@ module lsu import cvw::*; #(parameter cvw_t P) ( .HRDATA, .HWDATA(LSUHWDATA), .HWSTRB(LSUHWSTRB), .HSIZE(LSUHSIZE), .HBURST(LSUHBURST), .HTRANS(LSUHTRANS), .HWRITE(LSUHWRITE), .HREADY(LSUHREADY), .BeatCount, .SelBusBeat, .CacheReadDataWordM(DCacheReadDataWordM[P.LLEN-1:0]), .WriteDataM(LSUWriteDataM), - .Funct3(LSUFunct3M), .HADDR(LSUHADDR), .CacheBusAdr(DCacheBusAdr), .CacheBusRW, .CacheableOrFlushCacheM, + .Funct3(LSUFunct3M), .HADDR(LSUHADDR), .CacheBusAdr(DCacheBusAdr), .CacheBusRW, .BusCMOZero, .CacheableOrFlushCacheM, .CacheBusAck(DCacheBusAck), .FetchBuffer, .PAdr(PAdrM), .Cacheable(CacheableOrFlushCacheM), .BusRW, .Stall(GatedStallW), .BusStall, .BusCommitted(BusCommittedM)); From 69653e5faacff63c4165e0e4ef45a3a3bca7e881 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Mon, 27 Nov 2023 23:38:53 -0600 Subject: [PATCH 24/48] Fixed minor bug in the cbo hazard logic. --- src/ieu/controller.sv | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ieu/controller.sv b/src/ieu/controller.sv index a489b7f86..c9195132c 100644 --- a/src/ieu/controller.sv +++ b/src/ieu/controller.sv @@ -426,5 +426,8 @@ module controller import cvw::*; #(parameter cvw_t P) ( // atomic operations are also detected as MemRWD[1] //assign StoreStallD = MemRWE[0] & ((MemRWD[1] | (MemRWD[0] & P.DCACHE_SUPPORTED))); // *** RT: Modify for ZICBOZ - assign StoreStallD = (MemRWE[0] | (|CMOpE & P.ZICBOM_SUPPORTED)) & ((MemRWD[1] | (MemRWD[0] & P.DCACHE_SUPPORTED) | (|CMOpD & P.ZICBOM_SUPPORTED))); + logic cboD, cboE; + assign cboE = (|CMOpE[2:0] & P.ZICBOM_SUPPORTED) | (CMOpE[3] & P.ZICBOZ_SUPPORTED); + assign cboD = (|CMOpD[2:0] & P.ZICBOM_SUPPORTED) | (CMOpD[3] & P.ZICBOZ_SUPPORTED); + assign StoreStallD = (MemRWE[0] | cboE) & ((MemRWD[1] | (MemRWD[0] & P.DCACHE_SUPPORTED) | cboD)); endmodule From 9a24a5d9570f846ffb2c555932cdb369ebac73a1 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Tue, 28 Nov 2023 00:05:12 -0600 Subject: [PATCH 25/48] Renamed signal in pmachecker. --- src/mmu/adrdecs.sv | 8 ++++---- src/mmu/pmachecker.sv | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mmu/adrdecs.sv b/src/mmu/adrdecs.sv index 69b3a8c1d..922b98efb 100644 --- a/src/mmu/adrdecs.sv +++ b/src/mmu/adrdecs.sv @@ -30,17 +30,17 @@ module adrdecs import cvw::*; #(parameter cvw_t P) ( input logic [P.PA_BITS-1:0] PhysicalAddress, - input logic AccessRW, AccessRX, AccessRWXZ, AccessRWZ, AccessRXZ, + input logic AccessRW, AccessRX, AccessRWXZ, AccessRWC, AccessRXC, input logic [1:0] Size, output logic [11:0] SelRegions ); localparam logic [3:0] SUPPORTED_SIZE = (P.LLEN == 32 ? 4'b0111 : 4'b1111); // Determine which region of physical memory (if any) is being accessed - adrdec #(P.PA_BITS) dtimdec(PhysicalAddress, P.DTIM_BASE[P.PA_BITS-1:0], P.DTIM_RANGE[P.PA_BITS-1:0], P.DTIM_SUPPORTED, AccessRWZ, Size, SUPPORTED_SIZE, SelRegions[11]); - adrdec #(P.PA_BITS) iromdec(PhysicalAddress, P.IROM_BASE[P.PA_BITS-1:0], P.IROM_RANGE[P.PA_BITS-1:0], P.IROM_SUPPORTED, AccessRXZ, Size, SUPPORTED_SIZE, SelRegions[10]); + adrdec #(P.PA_BITS) dtimdec(PhysicalAddress, P.DTIM_BASE[P.PA_BITS-1:0], P.DTIM_RANGE[P.PA_BITS-1:0], P.DTIM_SUPPORTED, AccessRWC, Size, SUPPORTED_SIZE, SelRegions[11]); + adrdec #(P.PA_BITS) iromdec(PhysicalAddress, P.IROM_BASE[P.PA_BITS-1:0], P.IROM_RANGE[P.PA_BITS-1:0], P.IROM_SUPPORTED, AccessRXC, Size, SUPPORTED_SIZE, SelRegions[10]); adrdec #(P.PA_BITS) ddr4dec(PhysicalAddress, P.EXT_MEM_BASE[P.PA_BITS-1:0], P.EXT_MEM_RANGE[P.PA_BITS-1:0], P.EXT_MEM_SUPPORTED, AccessRWXZ, Size, SUPPORTED_SIZE, SelRegions[9]); - adrdec #(P.PA_BITS) bootromdec(PhysicalAddress, P.BOOTROM_BASE[P.PA_BITS-1:0], P.BOOTROM_RANGE[P.PA_BITS-1:0], P.BOOTROM_SUPPORTED, AccessRXZ, Size, SUPPORTED_SIZE, SelRegions[8]); + adrdec #(P.PA_BITS) bootromdec(PhysicalAddress, P.BOOTROM_BASE[P.PA_BITS-1:0], P.BOOTROM_RANGE[P.PA_BITS-1:0], P.BOOTROM_SUPPORTED, AccessRXC, Size, SUPPORTED_SIZE, SelRegions[8]); adrdec #(P.PA_BITS) uncoreramdec(PhysicalAddress, P.UNCORE_RAM_BASE[P.PA_BITS-1:0], P.UNCORE_RAM_RANGE[P.PA_BITS-1:0], P.UNCORE_RAM_SUPPORTED, AccessRWXZ, Size, SUPPORTED_SIZE, SelRegions[7]); adrdec #(P.PA_BITS) clintdec(PhysicalAddress, P.CLINT_BASE[P.PA_BITS-1:0], P.CLINT_RANGE[P.PA_BITS-1:0], P.CLINT_SUPPORTED, AccessRW, Size, SUPPORTED_SIZE, SelRegions[6]); adrdec #(P.PA_BITS) gpiodec(PhysicalAddress, P.GPIO_BASE[P.PA_BITS-1:0], P.GPIO_RANGE[P.PA_BITS-1:0], P.GPIO_SUPPORTED, AccessRW, Size, 4'b0100, SelRegions[5]); diff --git a/src/mmu/pmachecker.sv b/src/mmu/pmachecker.sv index a48ccd241..016d4defe 100644 --- a/src/mmu/pmachecker.sv +++ b/src/mmu/pmachecker.sv @@ -44,20 +44,20 @@ module pmachecker import cvw::*; #(parameter cvw_t P) ( ); logic PMAAccessFault; - logic AccessRW, AccessRWXZ, AccessRX, AccessRWZ, AccessRXZ; + logic AccessRW, AccessRWXZ, AccessRX, AccessRWC, AccessRXC; logic [11:0] SelRegions; logic AtomicAllowed; logic CacheableRegion, IdempotentRegion; // Determine what type of access is being made assign AccessRW = ReadAccessM | WriteAccessM; - assign AccessRWZ = AccessRW | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])); + assign AccessRWC = AccessRW | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])); assign AccessRWXZ = ReadAccessM | WriteAccessM | ExecuteAccessF | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])) | (P.ZICBOZ_SUPPORTED & (CMOp[3])); assign AccessRX = ReadAccessM | ExecuteAccessF; - assign AccessRXZ = AccessRX | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])); + assign AccessRXC = AccessRX | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])); // Determine which region of physical memory (if any) is being accessed - adrdecs #(P) adrdecs(PhysicalAddress, AccessRW, AccessRX, AccessRWXZ, AccessRWZ, AccessRXZ, Size, SelRegions); + adrdecs #(P) adrdecs(PhysicalAddress, AccessRW, AccessRX, AccessRWXZ, AccessRWC, AccessRXC, Size, SelRegions); // Only non-core RAM/ROM memory regions are cacheable. PBMT can override cachable; NC and IO are uncachable assign CacheableRegion = SelRegions[9] | SelRegions[8] | SelRegions[7]; // exclusion-tag: unused-cachable From 0229df4a0ff383e876f13c7b1d74524933b714a8 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Tue, 28 Nov 2023 01:03:48 -0600 Subject: [PATCH 26/48] Oups. Introduced undetected bug into the cache's cbo insructions. --- src/cache/cachefsm.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cache/cachefsm.sv b/src/cache/cachefsm.sv index e7e5e0306..f7edfb733 100644 --- a/src/cache/cachefsm.sv +++ b/src/cache/cachefsm.sv @@ -129,7 +129,7 @@ module cachefsm import cvw::*; #(parameter cvw_t P, STATE_READ_HOLD: if(Stall) NextState = STATE_READ_HOLD; else NextState = STATE_READY; // exclusion-tag-start: icache case - STATE_WRITEBACK: if(CacheBusAck & ~CMOp[3]) NextState = STATE_FETCH; + STATE_WRITEBACK: if(CacheBusAck & ~(|CMOp[3:1])) NextState = STATE_FETCH; else if(CacheBusAck) NextState = STATE_READ_HOLD; else NextState = STATE_WRITEBACK; // eviction needs a delay as the bus fsm does not correctly handle sending the write command at the same time as getting back the bus ack. From 4d4790ecf9a73ef93e628de619630a3110744bd7 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Tue, 28 Nov 2023 14:18:06 -0600 Subject: [PATCH 27/48] Optimizations to cclsm. --- src/lsu/align.sv | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/lsu/align.sv b/src/lsu/align.sv index ce704a316..a52ebdd02 100644 --- a/src/lsu/align.sv +++ b/src/lsu/align.sv @@ -65,12 +65,12 @@ module align import cvw::*; #(parameter cvw_t P) ( typedef enum logic [1:0] {STATE_READY, STATE_SPILL, STATE_STORE_DELAY} statetype; statetype CurrState, NextState; - logic TakeSpillM; + logic ValidSpillM; logic SpillM; logic SelSpillM; logic SpillSaveM; logic [P.LLEN-1:0] ReadDataWordFirstHalfM; - logic MisalignedM; + logic ValidMisalignedM, MisalignedM; logic [P.LLEN*2-1:0] ReadDataWordSpillAllM; logic [P.LLEN*2-1:0] ReadDataWordSpillShiftedM; @@ -78,7 +78,6 @@ module align import cvw::*; #(parameter cvw_t P) ( logic [(P.LLEN-1)*2/8:0] ByteMaskSaveM; logic [(P.LLEN-1)*2/8:0] ByteMaskMuxM; - logic SaveByteMask; logic HalfMisalignedM, WordMisalignedM; logic [OFFSET_BIT_POS-1:$clog2(LLENINBYTES)] WordOffsetM; logic [$clog2(LLENINBYTES)-1:0] ByteOffsetM; @@ -118,7 +117,7 @@ module align import cvw::*; #(parameter cvw_t P) ( assign WordMisalignedM = (ByteOffsetM[1:0] != '0) & Funct3M[1:0] == 2'b10; assign HalfSpillM = (IEUAdrM[OFFSET_BIT_POS-1:1] == '1) & HalfMisalignedM; assign WordSpillM = (IEUAdrM[OFFSET_BIT_POS-1:2] == '1) & WordMisalignedM; - assign ValidAccess = (|MemRWM) & ~SelHPTW; + assign ValidAccess = (|MemRWM); if(P.LLEN == 64) begin logic DoubleSpillM; @@ -126,15 +125,16 @@ module align import cvw::*; #(parameter cvw_t P) ( assign DoubleMisalignedM = (ByteOffsetM[2:0] != '0) & Funct3M[1:0] == 2'b11; assign DoubleSpillM = (IEUAdrM[OFFSET_BIT_POS-1:3] == '1) & DoubleMisalignedM; assign MisalignedM = ValidAccess & (HalfMisalignedM | WordMisalignedM | DoubleMisalignedM); - assign SpillM = ValidAccess & CacheableM & (HalfSpillM | WordSpillM | DoubleSpillM); + assign SpillM = ValidAccess & (HalfSpillM | WordSpillM | DoubleSpillM); end else begin - assign SpillM = ValidAccess & CacheableM & (HalfSpillM | WordSpillM); + assign SpillM = ValidAccess & (HalfSpillM | WordSpillM); assign MisalignedM = ValidAccess & (HalfMisalignedM | WordMisalignedM); end // align by shifting // Don't take the spill if there is a stall, TLB miss, or hardware update to the D/A bits - assign TakeSpillM = SpillM & ~CacheBusHPWTStall & ~(DTLBMissM | (P.SVADU_SUPPORTED & DataUpdateDAM)); + assign ValidSpillM = SpillM & ~CacheBusHPWTStall; + assign ValidMisalignedM = MisalignedM & ~SelHPTW; always_ff @(posedge clk) if (reset | FlushM) CurrState <= #1 STATE_READY; @@ -142,8 +142,8 @@ module align import cvw::*; #(parameter cvw_t P) ( always_comb begin case (CurrState) - STATE_READY: if (TakeSpillM & ~MemRWM[0]) NextState = STATE_SPILL; - else if(TakeSpillM & MemRWM[0])NextState = STATE_STORE_DELAY; + STATE_READY: if (ValidSpillM & ~MemRWM[0]) NextState = STATE_SPILL; + else if(ValidSpillM & MemRWM[0])NextState = STATE_STORE_DELAY; else NextState = STATE_READY; STATE_SPILL: if(StallM) NextState = STATE_SPILL; else NextState = STATE_READY; @@ -153,9 +153,8 @@ module align import cvw::*; #(parameter cvw_t P) ( end assign SelSpillM = (CurrState == STATE_SPILL | CurrState == STATE_STORE_DELAY); - assign SelSpillE = (CurrState == STATE_READY & TakeSpillM) | (CurrState == STATE_SPILL & CacheBusHPWTStall) | (CurrState == STATE_STORE_DELAY); - assign SaveByteMask = (CurrState == STATE_READY & TakeSpillM); - assign SpillSaveM = (CurrState == STATE_READY) & TakeSpillM & ~FlushM; + assign SelSpillE = (CurrState == STATE_READY & ValidSpillM) | (CurrState == STATE_SPILL & CacheBusHPWTStall) | (CurrState == STATE_STORE_DELAY); + assign SpillSaveM = (CurrState == STATE_READY) & ValidSpillM & ~FlushM; assign SelStoreDelay = (CurrState == STATE_STORE_DELAY); // *** Can this be merged into the PreLSURWM logic? assign SpillStallM = SelSpillE | CurrState == STATE_STORE_DELAY; mux2 #(2) memrwmux(MemRWM, 2'b00, SelStoreDelay, MemRWSpillM); @@ -173,14 +172,14 @@ module align import cvw::*; #(parameter cvw_t P) ( // shifter (4:1 mux for 32 bit, 8:1 mux for 64 bit) // 8 * is for shifting by bytes not bits - assign ReadDataWordSpillShiftedM = ReadDataWordSpillAllM >> (MisalignedM ? 8 * AccessByteOffsetM : '0); + assign ReadDataWordSpillShiftedM = ReadDataWordSpillAllM >> (ValidMisalignedM ? 8 * AccessByteOffsetM : '0); assign DCacheReadDataWordSpillM = ReadDataWordSpillShiftedM[P.LLEN-1:0]; // write path. Also has the 8:1 shifter muxing for the byteoffset // then it also has the mux to select when a spill occurs logic [P.LLEN*3-1:0] LSUWriteDataShiftedExtM; // *** RT: Find a better way. I've extending in both directions so we don't shift in zeros. The cache expects the writedata to not have any zero data, but instead replicated data. - assign LSUWriteDataShiftedExtM = {LSUWriteDataM, LSUWriteDataM, LSUWriteDataM} << (MisalignedM ? 8 * AccessByteOffsetM : '0); + assign LSUWriteDataShiftedExtM = {LSUWriteDataM, LSUWriteDataM, LSUWriteDataM} << (ValidMisalignedM ? 8 * AccessByteOffsetM : '0); assign LSUWriteDataSpillM = LSUWriteDataShiftedExtM[P.LLEN*3-1:P.LLEN]; mux3 #(2*P.LLEN/8) bytemaskspillmux(ByteMaskMuxM, // no spill @@ -188,6 +187,6 @@ module align import cvw::*; #(parameter cvw_t P) ( {{{P.LLEN/8}{1'b0}}, ByteMaskMuxM[P.LLEN*2/8-1:P.LLEN/8]}, // spill, second half {SelSpillM, SelSpillE}, ByteMaskSpillM); - flopenr #(P.LLEN*2/8) bytemaskreg(clk, reset, SaveByteMask, {ByteMaskExtendedM, ByteMaskM}, ByteMaskSaveM); + flopenr #(P.LLEN*2/8) bytemaskreg(clk, reset, SpillSaveM, {ByteMaskExtendedM, ByteMaskM}, ByteMaskSaveM); mux2 #(P.LLEN*2/8) bytemasksavemux({ByteMaskExtendedM, ByteMaskM}, ByteMaskSaveM, SelSpillM, ByteMaskMuxM); endmodule From df854280413d5f81c83f53d79413be29a94623ac Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Tue, 28 Nov 2023 14:19:30 -0600 Subject: [PATCH 28/48] More optimizations for cclsm. --- src/lsu/align.sv | 3 --- src/lsu/lsu.sv | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/lsu/align.sv b/src/lsu/align.sv index a52ebdd02..1f7f50ec4 100644 --- a/src/lsu/align.sv +++ b/src/lsu/align.sv @@ -37,7 +37,6 @@ module align import cvw::*; #(parameter cvw_t P) ( input logic [P.XLEN-1:0] IEUAdrE, // The next IEUAdrM input logic [2:0] Funct3M, // Size of memory operation input logic [1:0] MemRWM, - input logic CacheableM, input logic [P.LLEN*2-1:0] DCacheReadDataWordM, // Instruction from the IROM, I$, or bus. Used to check if the instruction if compressed input logic CacheBusHPWTStall, // I$ or bus are stalled. Transition to second fetch of spill after the first is fetched input logic DTLBMissM, // ITLB miss, ignore memory request @@ -54,7 +53,6 @@ module align import cvw::*; #(parameter cvw_t P) ( output logic [P.XLEN-1:0] IEUAdrSpillE, // The next PCF for one of the two memory addresses of the spill output logic [P.XLEN-1:0] IEUAdrSpillM, // IEUAdrM for one of the two memory addresses of the spill output logic SelSpillE, // During the transition between the two spill operations, the IFU should stall the pipeline - output logic [1:0] MemRWSpillM, output logic SelStoreDelay, //*** this is bad. really don't like moving this outside output logic [P.LLEN-1:0] DCacheReadDataWordSpillM, // The final 32 bit instruction after merging the two spilled fetches into 1 instruction output logic SpillStallM); @@ -157,7 +155,6 @@ module align import cvw::*; #(parameter cvw_t P) ( assign SpillSaveM = (CurrState == STATE_READY) & ValidSpillM & ~FlushM; assign SelStoreDelay = (CurrState == STATE_STORE_DELAY); // *** Can this be merged into the PreLSURWM logic? assign SpillStallM = SelSpillE | CurrState == STATE_STORE_DELAY; - mux2 #(2) memrwmux(MemRWM, 2'b00, SelStoreDelay, MemRWSpillM); //////////////////////////////////////////////////////////////////////////////////////////////////// // Merge spilled data diff --git a/src/lsu/lsu.sv b/src/lsu/lsu.sv index 6fe4377fc..81da2c075 100644 --- a/src/lsu/lsu.sv +++ b/src/lsu/lsu.sv @@ -159,10 +159,10 @@ module lsu import cvw::*; #(parameter cvw_t P) ( if(MISALIGN_SUPPORT) begin : ziccslm_align logic [P.XLEN-1:0] IEUAdrSpillE, IEUAdrSpillM; align #(P) align(.clk, .reset, .StallM, .FlushM, .IEUAdrE, .IEUAdrM, .Funct3M, - .MemRWM, .CacheableM, + .MemRWM, .DCacheReadDataWordM, .CacheBusHPWTStall, .DTLBMissM, .DataUpdateDAM, .SelHPTW, .ByteMaskM, .ByteMaskExtendedM, .LSUWriteDataM, .ByteMaskSpillM, .LSUWriteDataSpillM, - .IEUAdrSpillE, .IEUAdrSpillM, .SelSpillE, .MemRWSpillM, .DCacheReadDataWordSpillM, .SpillStallM, + .IEUAdrSpillE, .IEUAdrSpillM, .SelSpillE, .DCacheReadDataWordSpillM, .SpillStallM, .SelStoreDelay); assign IEUAdrExtM = {2'b00, IEUAdrSpillM}; assign IEUAdrExtE = {2'b00, IEUAdrSpillE}; From f4e77e96694722d678e551c2a92f1ac7b285ba0b Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Tue, 28 Nov 2023 14:21:37 -0600 Subject: [PATCH 29/48] Clean up. --- src/lsu/align.sv | 2 -- src/lsu/lsu.sv | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lsu/align.sv b/src/lsu/align.sv index 1f7f50ec4..645054058 100644 --- a/src/lsu/align.sv +++ b/src/lsu/align.sv @@ -39,8 +39,6 @@ module align import cvw::*; #(parameter cvw_t P) ( input logic [1:0] MemRWM, input logic [P.LLEN*2-1:0] DCacheReadDataWordM, // Instruction from the IROM, I$, or bus. Used to check if the instruction if compressed input logic CacheBusHPWTStall, // I$ or bus are stalled. Transition to second fetch of spill after the first is fetched - input logic DTLBMissM, // ITLB miss, ignore memory request - input logic DataUpdateDAM, // ITLB miss, ignore memory request input logic SelHPTW, input logic [(P.LLEN-1)/8:0] ByteMaskM, diff --git a/src/lsu/lsu.sv b/src/lsu/lsu.sv index 81da2c075..2b8a65ac6 100644 --- a/src/lsu/lsu.sv +++ b/src/lsu/lsu.sv @@ -160,7 +160,7 @@ module lsu import cvw::*; #(parameter cvw_t P) ( logic [P.XLEN-1:0] IEUAdrSpillE, IEUAdrSpillM; align #(P) align(.clk, .reset, .StallM, .FlushM, .IEUAdrE, .IEUAdrM, .Funct3M, .MemRWM, - .DCacheReadDataWordM, .CacheBusHPWTStall, .DTLBMissM, .DataUpdateDAM, .SelHPTW, + .DCacheReadDataWordM, .CacheBusHPWTStall, .SelHPTW, .ByteMaskM, .ByteMaskExtendedM, .LSUWriteDataM, .ByteMaskSpillM, .LSUWriteDataSpillM, .IEUAdrSpillE, .IEUAdrSpillM, .SelSpillE, .DCacheReadDataWordSpillM, .SpillStallM, .SelStoreDelay); From 865ebf8b9bb60dad52897dd42d06a9a1316ceb51 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Tue, 28 Nov 2023 19:41:46 -0600 Subject: [PATCH 30/48] cclsm cleanup. --- src/lsu/align.sv | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lsu/align.sv b/src/lsu/align.sv index 645054058..00261ca24 100644 --- a/src/lsu/align.sv +++ b/src/lsu/align.sv @@ -79,6 +79,7 @@ module align import cvw::*; #(parameter cvw_t P) ( logic [$clog2(LLENINBYTES)-1:0] ByteOffsetM; logic HalfSpillM, WordSpillM; logic [$clog2(LLENINBYTES)-1:0] AccessByteOffsetM; + logic [$clog2(LLENINBYTES)+2:0] ShiftAmount; logic ValidAccess; /* verilator lint_off WIDTHEXPAND */ @@ -167,14 +168,15 @@ module align import cvw::*; #(parameter cvw_t P) ( // shifter (4:1 mux for 32 bit, 8:1 mux for 64 bit) // 8 * is for shifting by bytes not bits - assign ReadDataWordSpillShiftedM = ReadDataWordSpillAllM >> (ValidMisalignedM ? 8 * AccessByteOffsetM : '0); + assign ShiftAmount = ValidMisalignedM ? 8 * AccessByteOffsetM : '0; + assign ReadDataWordSpillShiftedM = ReadDataWordSpillAllM >> ShiftAmount; assign DCacheReadDataWordSpillM = ReadDataWordSpillShiftedM[P.LLEN-1:0]; // write path. Also has the 8:1 shifter muxing for the byteoffset // then it also has the mux to select when a spill occurs logic [P.LLEN*3-1:0] LSUWriteDataShiftedExtM; // *** RT: Find a better way. I've extending in both directions so we don't shift in zeros. The cache expects the writedata to not have any zero data, but instead replicated data. - assign LSUWriteDataShiftedExtM = {LSUWriteDataM, LSUWriteDataM, LSUWriteDataM} << (ValidMisalignedM ? 8 * AccessByteOffsetM : '0); + assign LSUWriteDataShiftedExtM = {LSUWriteDataM, LSUWriteDataM, LSUWriteDataM} << ShiftAmount; assign LSUWriteDataSpillM = LSUWriteDataShiftedExtM[P.LLEN*3-1:P.LLEN]; mux3 #(2*P.LLEN/8) bytemaskspillmux(ByteMaskMuxM, // no spill From a69a70ba7fd8867a14a0ec30e643ae19fb14174b Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Tue, 28 Nov 2023 19:54:25 -0600 Subject: [PATCH 31/48] Removed unused hardware from alignment. --- src/lsu/align.sv | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/lsu/align.sv b/src/lsu/align.sv index 00261ca24..b3a20e9b4 100644 --- a/src/lsu/align.sv +++ b/src/lsu/align.sv @@ -65,22 +65,20 @@ module align import cvw::*; #(parameter cvw_t P) ( logic SpillM; logic SelSpillM; logic SpillSaveM; - logic [P.LLEN-1:0] ReadDataWordFirstHalfM; + logic [P.LLEN-1:0] ReadDataWordFirstHalfM; logic ValidMisalignedM, MisalignedM; logic [P.LLEN*2-1:0] ReadDataWordSpillAllM; logic [P.LLEN*2-1:0] ReadDataWordSpillShiftedM; - logic [P.XLEN-1:0] IEUAdrIncrementM; + logic [P.XLEN-1:0] IEUAdrIncrementM; - logic [(P.LLEN-1)*2/8:0] ByteMaskSaveM; - logic [(P.LLEN-1)*2/8:0] ByteMaskMuxM; - logic HalfMisalignedM, WordMisalignedM; + logic HalfMisalignedM, WordMisalignedM; logic [OFFSET_BIT_POS-1:$clog2(LLENINBYTES)] WordOffsetM; - logic [$clog2(LLENINBYTES)-1:0] ByteOffsetM; - logic HalfSpillM, WordSpillM; - logic [$clog2(LLENINBYTES)-1:0] AccessByteOffsetM; - logic [$clog2(LLENINBYTES)+2:0] ShiftAmount; - logic ValidAccess; + logic [$clog2(LLENINBYTES)-1:0] ByteOffsetM; + logic HalfSpillM, WordSpillM; + logic [$clog2(LLENINBYTES)-1:0] AccessByteOffsetM; + logic [$clog2(LLENINBYTES)+2:0] ShiftAmount; + logic ValidAccess; /* verilator lint_off WIDTHEXPAND */ assign IEUAdrIncrementM = IEUAdrM + LLENINBYTES; @@ -179,11 +177,9 @@ module align import cvw::*; #(parameter cvw_t P) ( assign LSUWriteDataShiftedExtM = {LSUWriteDataM, LSUWriteDataM, LSUWriteDataM} << ShiftAmount; assign LSUWriteDataSpillM = LSUWriteDataShiftedExtM[P.LLEN*3-1:P.LLEN]; - mux3 #(2*P.LLEN/8) bytemaskspillmux(ByteMaskMuxM, // no spill + mux3 #(2*P.LLEN/8) bytemaskspillmux({ByteMaskExtendedM, ByteMaskM}, // no spill {{{P.LLEN/8}{1'b0}}, ByteMaskM}, // spill, first half - {{{P.LLEN/8}{1'b0}}, ByteMaskMuxM[P.LLEN*2/8-1:P.LLEN/8]}, // spill, second half + {{{P.LLEN/8}{1'b0}}, ByteMaskExtendedM}, // spill, second half {SelSpillM, SelSpillE}, ByteMaskSpillM); - flopenr #(P.LLEN*2/8) bytemaskreg(clk, reset, SpillSaveM, {ByteMaskExtendedM, ByteMaskM}, ByteMaskSaveM); - mux2 #(P.LLEN*2/8) bytemasksavemux({ByteMaskExtendedM, ByteMaskM}, ByteMaskSaveM, SelSpillM, ByteMaskMuxM); endmodule From 143c6ca4d190aff8b288429c15112a65256e3fe7 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Tue, 28 Nov 2023 22:28:11 -0600 Subject: [PATCH 32/48] Simplification to alignment. --- src/lsu/align.sv | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/src/lsu/align.sv b/src/lsu/align.sv index b3a20e9b4..91eda1b8c 100644 --- a/src/lsu/align.sv +++ b/src/lsu/align.sv @@ -72,13 +72,10 @@ module align import cvw::*; #(parameter cvw_t P) ( logic [P.XLEN-1:0] IEUAdrIncrementM; - logic HalfMisalignedM, WordMisalignedM; - logic [OFFSET_BIT_POS-1:$clog2(LLENINBYTES)] WordOffsetM; - logic [$clog2(LLENINBYTES)-1:0] ByteOffsetM; - logic HalfSpillM, WordSpillM; logic [$clog2(LLENINBYTES)-1:0] AccessByteOffsetM; logic [$clog2(LLENINBYTES)+2:0] ShiftAmount; logic ValidAccess; + logic PotentialSpillM; /* verilator lint_off WIDTHEXPAND */ assign IEUAdrIncrementM = IEUAdrM + LLENINBYTES; @@ -95,36 +92,27 @@ module align import cvw::*; #(parameter cvw_t P) ( // 2) offset // 3) access location within the cacheline - assign {WordOffsetM, ByteOffsetM} = IEUAdrM[OFFSET_BIT_POS-1:0]; - always_comb begin case (Funct3M[1:0]) 2'b00: AccessByteOffsetM = '0; // byte access - 2'b01: AccessByteOffsetM = {2'b00, ByteOffsetM[0]}; // half access - 2'b10: AccessByteOffsetM = {1'b0, ByteOffsetM[1:0]}; // word access - 2'b11: AccessByteOffsetM = ByteOffsetM; // double access - default: AccessByteOffsetM = ByteOffsetM; + 2'b01: AccessByteOffsetM = {2'b00, IEUAdrM[0]}; // half access + 2'b10: AccessByteOffsetM = {1'b0, IEUAdrM[1:0]}; // word access + 2'b11: AccessByteOffsetM = IEUAdrM[2:0]; // double access + default: AccessByteOffsetM = IEUAdrM[2:0]; + endcase + case (Funct3M[1:0]) + 2'b00: PotentialSpillM = '0; // byte access + 2'b01: PotentialSpillM = IEUAdrM[OFFSET_BIT_POS-1:1] == '1; // half access + 2'b10: PotentialSpillM = IEUAdrM[OFFSET_BIT_POS-1:2] == '1; // word access + 2'b11: PotentialSpillM = IEUAdrM[OFFSET_BIT_POS-1:3] == '1; // double access + default: PotentialSpillM = '0; endcase end + assign MisalignedM = ValidAccess & (AccessByteOffsetM != '0); + assign SpillM = MisalignedM & PotentialSpillM; // compute misalignement - assign HalfMisalignedM = (ByteOffsetM[0] != '0) & Funct3M[1:0] == 2'b01; - assign WordMisalignedM = (ByteOffsetM[1:0] != '0) & Funct3M[1:0] == 2'b10; - assign HalfSpillM = (IEUAdrM[OFFSET_BIT_POS-1:1] == '1) & HalfMisalignedM; - assign WordSpillM = (IEUAdrM[OFFSET_BIT_POS-1:2] == '1) & WordMisalignedM; assign ValidAccess = (|MemRWM); - - if(P.LLEN == 64) begin - logic DoubleSpillM; - logic DoubleMisalignedM; - assign DoubleMisalignedM = (ByteOffsetM[2:0] != '0) & Funct3M[1:0] == 2'b11; - assign DoubleSpillM = (IEUAdrM[OFFSET_BIT_POS-1:3] == '1) & DoubleMisalignedM; - assign MisalignedM = ValidAccess & (HalfMisalignedM | WordMisalignedM | DoubleMisalignedM); - assign SpillM = ValidAccess & (HalfSpillM | WordSpillM | DoubleSpillM); - end else begin - assign SpillM = ValidAccess & (HalfSpillM | WordSpillM); - assign MisalignedM = ValidAccess & (HalfMisalignedM | WordMisalignedM); - end // align by shifting // Don't take the spill if there is a stall, TLB miss, or hardware update to the D/A bits From 4149ae6c11aeb58198e8a8d89ace92810bfa82a0 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Tue, 28 Nov 2023 23:05:47 -0600 Subject: [PATCH 33/48] More cleanup. --- src/lsu/align.sv | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/lsu/align.sv b/src/lsu/align.sv index 91eda1b8c..a3e2dadac 100644 --- a/src/lsu/align.sv +++ b/src/lsu/align.sv @@ -66,7 +66,7 @@ module align import cvw::*; #(parameter cvw_t P) ( logic SelSpillM; logic SpillSaveM; logic [P.LLEN-1:0] ReadDataWordFirstHalfM; - logic ValidMisalignedM, MisalignedM; + logic MisalignedM; logic [P.LLEN*2-1:0] ReadDataWordSpillAllM; logic [P.LLEN*2-1:0] ReadDataWordSpillShiftedM; @@ -92,6 +92,9 @@ module align import cvw::*; #(parameter cvw_t P) ( // 2) offset // 3) access location within the cacheline + assign ValidAccess = (|MemRWM); + + // compute misalignement always_comb begin case (Funct3M[1:0]) 2'b00: AccessByteOffsetM = '0; // byte access @@ -110,14 +113,8 @@ module align import cvw::*; #(parameter cvw_t P) ( end assign MisalignedM = ValidAccess & (AccessByteOffsetM != '0); assign SpillM = MisalignedM & PotentialSpillM; - - // compute misalignement - assign ValidAccess = (|MemRWM); - // align by shifting - // Don't take the spill if there is a stall, TLB miss, or hardware update to the D/A bits - assign ValidSpillM = SpillM & ~CacheBusHPWTStall; - assign ValidMisalignedM = MisalignedM & ~SelHPTW; + assign ValidSpillM = SpillM & ~CacheBusHPWTStall; // Don't take the spill if there is a stall always_ff @(posedge clk) if (reset | FlushM) CurrState <= #1 STATE_READY; @@ -154,7 +151,7 @@ module align import cvw::*; #(parameter cvw_t P) ( // shifter (4:1 mux for 32 bit, 8:1 mux for 64 bit) // 8 * is for shifting by bytes not bits - assign ShiftAmount = ValidMisalignedM ? 8 * AccessByteOffsetM : '0; + assign ShiftAmount = MisalignedM & ~SelHPTW ? {AccessByteOffsetM, 3'b0} : '0; // AND gate assign ReadDataWordSpillShiftedM = ReadDataWordSpillAllM >> ShiftAmount; assign DCacheReadDataWordSpillM = ReadDataWordSpillShiftedM[P.LLEN-1:0]; From d29b2b95f7b059063b912f2ec9754f5c6032038a Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Tue, 28 Nov 2023 23:28:50 -0600 Subject: [PATCH 34/48] Additional cleanup. --- src/lsu/align.sv | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/lsu/align.sv b/src/lsu/align.sv index a3e2dadac..c618bb605 100644 --- a/src/lsu/align.sv +++ b/src/lsu/align.sv @@ -62,7 +62,6 @@ module align import cvw::*; #(parameter cvw_t P) ( statetype CurrState, NextState; logic ValidSpillM; - logic SpillM; logic SelSpillM; logic SpillSaveM; logic [P.LLEN-1:0] ReadDataWordFirstHalfM; @@ -74,7 +73,6 @@ module align import cvw::*; #(parameter cvw_t P) ( logic [$clog2(LLENINBYTES)-1:0] AccessByteOffsetM; logic [$clog2(LLENINBYTES)+2:0] ShiftAmount; - logic ValidAccess; logic PotentialSpillM; /* verilator lint_off WIDTHEXPAND */ @@ -92,8 +90,6 @@ module align import cvw::*; #(parameter cvw_t P) ( // 2) offset // 3) access location within the cacheline - assign ValidAccess = (|MemRWM); - // compute misalignement always_comb begin case (Funct3M[1:0]) @@ -111,10 +107,9 @@ module align import cvw::*; #(parameter cvw_t P) ( default: PotentialSpillM = '0; endcase end - assign MisalignedM = ValidAccess & (AccessByteOffsetM != '0); - assign SpillM = MisalignedM & PotentialSpillM; + assign MisalignedM = (|MemRWM) & (AccessByteOffsetM != '0); - assign ValidSpillM = SpillM & ~CacheBusHPWTStall; // Don't take the spill if there is a stall + assign ValidSpillM = MisalignedM & PotentialSpillM & ~CacheBusHPWTStall; // Don't take the spill if there is a stall always_ff @(posedge clk) if (reset | FlushM) CurrState <= #1 STATE_READY; From 053b0946201921c928e1e6b5bcec34e9dfada1f9 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Wed, 29 Nov 2023 12:26:18 -0600 Subject: [PATCH 35/48] Simpilified pmachecker for cmo. --- src/mmu/adrdecs.sv | 12 ++++++------ src/mmu/pmachecker.sv | 10 ++++------ src/uncore/uncore.sv | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/mmu/adrdecs.sv b/src/mmu/adrdecs.sv index 922b98efb..576bb21b8 100644 --- a/src/mmu/adrdecs.sv +++ b/src/mmu/adrdecs.sv @@ -30,18 +30,18 @@ module adrdecs import cvw::*; #(parameter cvw_t P) ( input logic [P.PA_BITS-1:0] PhysicalAddress, - input logic AccessRW, AccessRX, AccessRWXZ, AccessRWC, AccessRXC, + input logic AccessRW, AccessRX, AccessRWXC, input logic [1:0] Size, output logic [11:0] SelRegions ); localparam logic [3:0] SUPPORTED_SIZE = (P.LLEN == 32 ? 4'b0111 : 4'b1111); // Determine which region of physical memory (if any) is being accessed - adrdec #(P.PA_BITS) dtimdec(PhysicalAddress, P.DTIM_BASE[P.PA_BITS-1:0], P.DTIM_RANGE[P.PA_BITS-1:0], P.DTIM_SUPPORTED, AccessRWC, Size, SUPPORTED_SIZE, SelRegions[11]); - adrdec #(P.PA_BITS) iromdec(PhysicalAddress, P.IROM_BASE[P.PA_BITS-1:0], P.IROM_RANGE[P.PA_BITS-1:0], P.IROM_SUPPORTED, AccessRXC, Size, SUPPORTED_SIZE, SelRegions[10]); - adrdec #(P.PA_BITS) ddr4dec(PhysicalAddress, P.EXT_MEM_BASE[P.PA_BITS-1:0], P.EXT_MEM_RANGE[P.PA_BITS-1:0], P.EXT_MEM_SUPPORTED, AccessRWXZ, Size, SUPPORTED_SIZE, SelRegions[9]); - adrdec #(P.PA_BITS) bootromdec(PhysicalAddress, P.BOOTROM_BASE[P.PA_BITS-1:0], P.BOOTROM_RANGE[P.PA_BITS-1:0], P.BOOTROM_SUPPORTED, AccessRXC, Size, SUPPORTED_SIZE, SelRegions[8]); - adrdec #(P.PA_BITS) uncoreramdec(PhysicalAddress, P.UNCORE_RAM_BASE[P.PA_BITS-1:0], P.UNCORE_RAM_RANGE[P.PA_BITS-1:0], P.UNCORE_RAM_SUPPORTED, AccessRWXZ, Size, SUPPORTED_SIZE, SelRegions[7]); + adrdec #(P.PA_BITS) dtimdec(PhysicalAddress, P.DTIM_BASE[P.PA_BITS-1:0], P.DTIM_RANGE[P.PA_BITS-1:0], P.DTIM_SUPPORTED, AccessRW, Size, SUPPORTED_SIZE, SelRegions[11]); + adrdec #(P.PA_BITS) iromdec(PhysicalAddress, P.IROM_BASE[P.PA_BITS-1:0], P.IROM_RANGE[P.PA_BITS-1:0], P.IROM_SUPPORTED, AccessRX, Size, SUPPORTED_SIZE, SelRegions[10]); + adrdec #(P.PA_BITS) ddr4dec(PhysicalAddress, P.EXT_MEM_BASE[P.PA_BITS-1:0], P.EXT_MEM_RANGE[P.PA_BITS-1:0], P.EXT_MEM_SUPPORTED, AccessRWXC, Size, SUPPORTED_SIZE, SelRegions[9]); + adrdec #(P.PA_BITS) bootromdec(PhysicalAddress, P.BOOTROM_BASE[P.PA_BITS-1:0], P.BOOTROM_RANGE[P.PA_BITS-1:0], P.BOOTROM_SUPPORTED, AccessRX, Size, SUPPORTED_SIZE, SelRegions[8]); + adrdec #(P.PA_BITS) uncoreramdec(PhysicalAddress, P.UNCORE_RAM_BASE[P.PA_BITS-1:0], P.UNCORE_RAM_RANGE[P.PA_BITS-1:0], P.UNCORE_RAM_SUPPORTED, AccessRWXC, Size, SUPPORTED_SIZE, SelRegions[7]); adrdec #(P.PA_BITS) clintdec(PhysicalAddress, P.CLINT_BASE[P.PA_BITS-1:0], P.CLINT_RANGE[P.PA_BITS-1:0], P.CLINT_SUPPORTED, AccessRW, Size, SUPPORTED_SIZE, SelRegions[6]); adrdec #(P.PA_BITS) gpiodec(PhysicalAddress, P.GPIO_BASE[P.PA_BITS-1:0], P.GPIO_RANGE[P.PA_BITS-1:0], P.GPIO_SUPPORTED, AccessRW, Size, 4'b0100, SelRegions[5]); adrdec #(P.PA_BITS) uartdec(PhysicalAddress, P.UART_BASE[P.PA_BITS-1:0], P.UART_RANGE[P.PA_BITS-1:0], P.UART_SUPPORTED, AccessRW, Size, 4'b0001, SelRegions[4]); diff --git a/src/mmu/pmachecker.sv b/src/mmu/pmachecker.sv index 016d4defe..f88479753 100644 --- a/src/mmu/pmachecker.sv +++ b/src/mmu/pmachecker.sv @@ -44,20 +44,18 @@ module pmachecker import cvw::*; #(parameter cvw_t P) ( ); logic PMAAccessFault; - logic AccessRW, AccessRWXZ, AccessRX, AccessRWC, AccessRXC; + logic AccessRW, AccessRWXC, AccessRX; logic [11:0] SelRegions; logic AtomicAllowed; logic CacheableRegion, IdempotentRegion; // Determine what type of access is being made assign AccessRW = ReadAccessM | WriteAccessM; - assign AccessRWC = AccessRW | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])); - assign AccessRWXZ = ReadAccessM | WriteAccessM | ExecuteAccessF | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])) | (P.ZICBOZ_SUPPORTED & (CMOp[3])); + assign AccessRWXC = ReadAccessM | WriteAccessM | ExecuteAccessF | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])) | (P.ZICBOZ_SUPPORTED & (CMOp[3])); assign AccessRX = ReadAccessM | ExecuteAccessF; - assign AccessRXC = AccessRX | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])); // Determine which region of physical memory (if any) is being accessed - adrdecs #(P) adrdecs(PhysicalAddress, AccessRW, AccessRX, AccessRWXZ, AccessRWC, AccessRXC, Size, SelRegions); + adrdecs #(P) adrdecs(PhysicalAddress, AccessRW, AccessRX, AccessRWXC, Size, SelRegions); // Only non-core RAM/ROM memory regions are cacheable. PBMT can override cachable; NC and IO are uncachable assign CacheableRegion = SelRegions[9] | SelRegions[8] | SelRegions[7]; // exclusion-tag: unused-cachable @@ -74,7 +72,7 @@ module pmachecker import cvw::*; #(parameter cvw_t P) ( assign SelTIM = SelRegions[11] | SelRegions[10]; // exclusion-tag: unused-idempotent // Detect access faults - assign PMAAccessFault = (SelRegions[0]) & AccessRWXZ | AtomicAccessM & ~AtomicAllowed; + assign PMAAccessFault = (SelRegions[0]) & AccessRWXC | AtomicAccessM & ~AtomicAllowed; assign PMAInstrAccessFaultF = ExecuteAccessF & PMAAccessFault; assign PMALoadAccessFaultM = ReadAccessM & PMAAccessFault; assign PMAStoreAmoAccessFaultM = (WriteAccessM | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])) | (P.ZICBOZ_SUPPORTED & CMOp[3])) & PMAAccessFault; diff --git a/src/uncore/uncore.sv b/src/uncore/uncore.sv index ee93c2904..60d197f78 100644 --- a/src/uncore/uncore.sv +++ b/src/uncore/uncore.sv @@ -88,7 +88,7 @@ module uncore import cvw::*; #(parameter cvw_t P)( // Determine which region of physical memory (if any) is being accessed // Use a trimmed down portion of the PMA checker - only the address decoders // Set access types to all 1 as don't cares because the MMU has already done access checking - adrdecs #(P) adrdecs(HADDR, 1'b1, 1'b1, 1'b1, 1'b1, 1'b1, HSIZE[1:0], HSELRegions); + adrdecs #(P) adrdecs(HADDR, 1'b1, 1'b1, 1'b1, HSIZE[1:0], HSELRegions); // unswizzle HSEL signals assign {HSELDTIM, HSELIROM, HSELEXT, HSELBootRom, HSELRam, HSELCLINT, HSELGPIO, HSELUART, HSELPLIC, HSELEXTSDC, HSELSPI} = HSELRegions[11:1]; From 80336493f5f16329a03768f71564381bb5f9e126 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Wed, 29 Nov 2023 15:20:49 -0600 Subject: [PATCH 36/48] Cleaned up redundant ZICBOM/Z_SUPPORTED. --- src/cache/cachefsm.sv | 24 ++++++++++++------------ src/ieu/controller.sv | 10 ++++------ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/cache/cachefsm.sv b/src/cache/cachefsm.sv index f7edfb733..b8f2130f7 100644 --- a/src/cache/cachefsm.sv +++ b/src/cache/cachefsm.sv @@ -94,7 +94,7 @@ module cachefsm import cvw::*; #(parameter cvw_t P, assign AnyMiss = (CacheRW[0] | CacheRW[1]) & ~CacheHit & ~InvalidateCache; // exclusion-tag: cache AnyMiss assign AnyUpdateHit = (CacheRW[0]) & CacheHit; // exclusion-tag: icache storeAMO1 assign AnyHit = AnyUpdateHit | (CacheRW[1] & CacheHit); // exclusion-tag: icache AnyUpdateHit - assign CMOWritebackHit = (CMOp[1] | CMOp[2]) & CacheHit; + assign CMOWritebackHit = (CMOp[1] | CMOp[2]) & CacheHit; // *** why does this not include dirty? assign CMOZeroNoEviction = CMOp[3] & ~LineDirty; // (hit or miss) with no writeback store zeros now assign CMOZeroEviction = CMOp[3] & LineDirty; // (hit or miss) with writeback dirty line assign CMOWriteback = CMOWritebackHit | CMOZeroEviction; @@ -130,7 +130,7 @@ module cachefsm import cvw::*; #(parameter cvw_t P, else NextState = STATE_READY; // exclusion-tag-start: icache case STATE_WRITEBACK: if(CacheBusAck & ~(|CMOp[3:1])) NextState = STATE_FETCH; - else if(CacheBusAck) NextState = STATE_READ_HOLD; + else if(CacheBusAck) NextState = STATE_READ_HOLD; // *** why not Ready? else NextState = STATE_WRITEBACK; // eviction needs a delay as the bus fsm does not correctly handle sending the write command at the same time as getting back the bus ack. STATE_FLUSH: if(LineDirty) NextState = STATE_FLUSH_WRITEBACK; @@ -154,24 +154,24 @@ module cachefsm import cvw::*; #(parameter cvw_t P, (CurrState == STATE_FLUSH_WRITEBACK); // write enables internal to cache assign SetValid = CurrState == STATE_WRITE_LINE | - (P.ZICBOZ_SUPPORTED & CurrState == STATE_READY & CMOZeroNoEviction) | - (P.ZICBOZ_SUPPORTED & CurrState == STATE_WRITEBACK & CacheBusAck & CMOp[3]); - assign ClearValid = P.ZICBOM_SUPPORTED & ((CurrState == STATE_READY & CMOp[0] & CacheHit) | - (CurrState == STATE_WRITEBACK & CMOp[2] & CacheBusAck)); + (CurrState == STATE_READY & CMOZeroNoEviction) | + (CurrState == STATE_WRITEBACK & CacheBusAck & CMOp[3]); + assign ClearValid = (CurrState == STATE_READY & CMOp[0]) | + (CurrState == STATE_WRITEBACK & CMOp[2] & CacheBusAck); // coverage off -item e 1 -fecexprrow 8 assign LRUWriteEn = (((CurrState == STATE_READY & (AnyHit | CMOZeroNoEviction)) | (CurrState == STATE_WRITE_LINE)) & ~FlushStage) | - (P.ZICBOZ_SUPPORTED & CurrState == STATE_WRITEBACK & CMOp[3] & CacheBusAck); + (CurrState == STATE_WRITEBACK & CMOp[3] & CacheBusAck); // exclusion-tag-start: icache flushdirtycontrols assign SetDirty = (CurrState == STATE_READY & (AnyUpdateHit | CMOZeroNoEviction)) | // exclusion-tag: icache SetDirty (CurrState == STATE_WRITE_LINE & (CacheRW[0])) | - (P.ZICBOZ_SUPPORTED & CurrState == STATE_WRITEBACK & (CMOp[3] & CacheBusAck)); + (CurrState == STATE_WRITEBACK & (CMOp[3] & CacheBusAck)); assign ClearDirty = (CurrState == STATE_WRITE_LINE & ~(CacheRW[0])) | // exclusion-tag: icache ClearDirty (CurrState == STATE_FLUSH & LineDirty) | // This is wrong in a multicore snoop cache protocal. Dirty must be cleared concurrently and atomically with writeback. For single core cannot clear after writeback on bus ack and change flushadr. Clears the wrong set. // Flush and eviction controls - (P.ZICBOM_SUPPORTED & CurrState == STATE_WRITEBACK & (CMOp[1] | CMOp[2]) & CacheBusAck); - assign SelWay = (CurrState == STATE_WRITEBACK & ((~CacheBusAck & ~(CMOp[1] | CMOp[2])) | (P.ZICBOZ_SUPPORTED & CacheBusAck & CMOp[3]))) | - (CurrState == STATE_READY & ((AnyMiss & LineDirty) | (P.ZICBOZ_SUPPORTED & CMOZeroNoEviction & ~CacheHit))) | + CurrState == STATE_WRITEBACK & (CMOp[1] | CMOp[2]) & CacheBusAck; + assign SelWay = (CurrState == STATE_WRITEBACK & ((~CacheBusAck & ~(CMOp[1] | CMOp[2])) | (CacheBusAck & CMOp[3]))) | + (CurrState == STATE_READY & ((AnyMiss & LineDirty) | (CMOZeroNoEviction & ~CacheHit))) | (CurrState == STATE_WRITE_LINE); assign SelWriteback = (CurrState == STATE_WRITEBACK & (CMOp[1] | CMOp[2] | ~CacheBusAck)) | (CurrState == STATE_READY & AnyMiss & LineDirty); @@ -194,7 +194,7 @@ module cachefsm import cvw::*; #(parameter cvw_t P, assign CacheBusRW[0] = (CurrState == STATE_READY & AnyMiss & LineDirty) | // exclusion-tag: icache CacheBusW (CurrState == STATE_WRITEBACK & ~CacheBusAck) | (CurrState == STATE_FLUSH_WRITEBACK & ~CacheBusAck) | - (P.ZICBOM_SUPPORTED & CurrState == STATE_WRITEBACK & (CMOp[1] | CMOp[2]) & ~CacheBusAck); + (CurrState == STATE_WRITEBACK & (CMOp[1] | CMOp[2]) & ~CacheBusAck); assign SelAdr = (CurrState == STATE_READY & (CacheRW[0] | AnyMiss | (|CMOp))) | // exclusion-tag: icache SelAdrCauses // changes if store delay hazard removed (CurrState == STATE_FETCH) | diff --git a/src/ieu/controller.sv b/src/ieu/controller.sv index c9195132c..41be3941a 100644 --- a/src/ieu/controller.sv +++ b/src/ieu/controller.sv @@ -357,8 +357,10 @@ module controller import cvw::*; #(parameter cvw_t P) ( // Cache Management instructions always_comb begin CMOpD = 4'b0000; // default: not a cbo instruction - if ((P.ZICBOM_SUPPORTED | P.ZICBOZ_SUPPORTED) & CMOD) begin + if ((P.ZICBOZ_SUPPORTED) & CMOD) begin CMOpD[3] = (InstrD[31:20] == 12'd4); // cbo.zero + end + if ((P.ZICBOM_SUPPORTED) & CMOD) begin CMOpD[2] = (InstrD[31:20] == 12'd2); // cbo.clean CMOpD[1] = (InstrD[31:20] == 12'd1) | ((InstrD[31:20] == 12'd0) & (ENVCFG_CBE[1:0] == 2'b01)); // cbo.flush CMOpD[0] = (InstrD[31:20] == 12'd0) & (ENVCFG_CBE[1:0] == 2'b11); // cbo.inval @@ -425,9 +427,5 @@ module controller import cvw::*; #(parameter cvw_t P) ( // a cache cannot read or write immediately after a write // atomic operations are also detected as MemRWD[1] //assign StoreStallD = MemRWE[0] & ((MemRWD[1] | (MemRWD[0] & P.DCACHE_SUPPORTED))); - // *** RT: Modify for ZICBOZ - logic cboD, cboE; - assign cboE = (|CMOpE[2:0] & P.ZICBOM_SUPPORTED) | (CMOpE[3] & P.ZICBOZ_SUPPORTED); - assign cboD = (|CMOpD[2:0] & P.ZICBOM_SUPPORTED) | (CMOpD[3] & P.ZICBOZ_SUPPORTED); - assign StoreStallD = (MemRWE[0] | cboE) & ((MemRWD[1] | (MemRWD[0] & P.DCACHE_SUPPORTED) | cboD)); + assign StoreStallD = (MemRWE[0] | (|CMOpE)) & ((MemRWD[1] | (MemRWD[0] & P.DCACHE_SUPPORTED) | (|CMOpD))); endmodule From fc04b6f7d87319ec8e601a6e9e1261bd84acc359 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Wed, 29 Nov 2023 15:39:39 -0600 Subject: [PATCH 37/48] Removed redundant ZICBOM/Z_SUPPORTED from pmachecker. --- src/mmu/pmachecker.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mmu/pmachecker.sv b/src/mmu/pmachecker.sv index f88479753..3c23d3623 100644 --- a/src/mmu/pmachecker.sv +++ b/src/mmu/pmachecker.sv @@ -51,7 +51,7 @@ module pmachecker import cvw::*; #(parameter cvw_t P) ( // Determine what type of access is being made assign AccessRW = ReadAccessM | WriteAccessM; - assign AccessRWXC = ReadAccessM | WriteAccessM | ExecuteAccessF | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])) | (P.ZICBOZ_SUPPORTED & (CMOp[3])); + assign AccessRWXC = ReadAccessM | WriteAccessM | ExecuteAccessF | (|CMOp); assign AccessRX = ReadAccessM | ExecuteAccessF; // Determine which region of physical memory (if any) is being accessed @@ -75,5 +75,5 @@ module pmachecker import cvw::*; #(parameter cvw_t P) ( assign PMAAccessFault = (SelRegions[0]) & AccessRWXC | AtomicAccessM & ~AtomicAllowed; assign PMAInstrAccessFaultF = ExecuteAccessF & PMAAccessFault; assign PMALoadAccessFaultM = ReadAccessM & PMAAccessFault; - assign PMAStoreAmoAccessFaultM = (WriteAccessM | (P.ZICBOM_SUPPORTED & (|CMOp[2:0])) | (P.ZICBOZ_SUPPORTED & CMOp[3])) & PMAAccessFault; + assign PMAStoreAmoAccessFaultM = (WriteAccessM | (|CMOp)) & PMAAccessFault; endmodule From f4e4aac8b5c78120e1f62a8fbc03470903b389ff Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Wed, 29 Nov 2023 16:09:31 -0600 Subject: [PATCH 38/48] Added CMOp to pmp checker --- src/mmu/mmu.sv | 2 +- src/mmu/pmpchecker.sv | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mmu/mmu.sv b/src/mmu/mmu.sv index 16016ac47..f9af52139 100644 --- a/src/mmu/mmu.sv +++ b/src/mmu/mmu.sv @@ -115,7 +115,7 @@ module mmu import cvw::*; #(parameter cvw_t P, if (P.PMP_ENTRIES > 0) begin : pmp pmpchecker #(P) pmpchecker(.PhysicalAddress, .PrivilegeModeW, .PMPCFG_ARRAY_REGW, .PMPADDR_ARRAY_REGW, - .ExecuteAccessF, .WriteAccessM, .ReadAccessM, + .ExecuteAccessF, .WriteAccessM, .ReadAccessM, .CMOp, .PMPInstrAccessFaultF, .PMPLoadAccessFaultM, .PMPStoreAmoAccessFaultM); end else begin assign PMPInstrAccessFaultF = 0; diff --git a/src/mmu/pmpchecker.sv b/src/mmu/pmpchecker.sv index 89c22c486..ddd7e72b0 100644 --- a/src/mmu/pmpchecker.sv +++ b/src/mmu/pmpchecker.sv @@ -42,6 +42,7 @@ module pmpchecker import cvw::*; #(parameter cvw_t P) ( input var logic [7:0] PMPCFG_ARRAY_REGW[P.PMP_ENTRIES-1:0], input var logic [P.PA_BITS-3:0] PMPADDR_ARRAY_REGW [P.PMP_ENTRIES-1:0], input logic ExecuteAccessF, WriteAccessM, ReadAccessM, + input logic [3:0] CMOp, output logic PMPInstrAccessFaultF, output logic PMPLoadAccessFaultM, output logic PMPStoreAmoAccessFaultM @@ -53,6 +54,8 @@ module pmpchecker import cvw::*; #(parameter cvw_t P) ( logic [P.PMP_ENTRIES-1:0] FirstMatch; // onehot encoding for the first pmpaddr to match the current address. logic [P.PMP_ENTRIES-1:0] L, X, W, R; // PMP matches and has flag set logic [P.PMP_ENTRIES-1:0] PAgePMPAdr; // for TOR PMP matching, PhysicalAddress > PMPAdr[i] + logic PMPCMOAccessFault, PMPCBOMAccessFault, PMPCBOZAccessFault; + if (P.PMP_ENTRIES > 0) begin: pmp // prevent complaints about array of no elements when PMP_ENTRIES = 0 pmpadrdec #(P) pmpadrdecs[P.PMP_ENTRIES-1:0]( @@ -69,7 +72,11 @@ module pmpchecker import cvw::*; #(parameter cvw_t P) ( // Only enforce PMP checking for S and U modes or in Machine mode when L bit is set in selected region assign EnforcePMP = (PrivilegeModeW != P.M_MODE) | (|(L & FirstMatch)); // *** switch to this logic when PMP is initialized for non-machine mode + assign PMPCBOMAccessFault = EnforcePMP & (|CMOp[2:0]) & ~|((R|W) & FirstMatch) ; + assign PMPCBOZAccessFault = EnforcePMP & CMOp[3] & ~|(W & FirstMatch) ; + assign PMPCMOAccessFault = PMPCBOZAccessFault | PMPCBOMAccessFault; + assign PMPInstrAccessFaultF = EnforcePMP & ExecuteAccessF & ~|(X & FirstMatch) ; - assign PMPStoreAmoAccessFaultM = EnforcePMP & WriteAccessM & ~|(W & FirstMatch) ; + assign PMPStoreAmoAccessFaultM = (EnforcePMP & WriteAccessM & ~|(W & FirstMatch)) | PMPCMOAccessFault; assign PMPLoadAccessFaultM = EnforcePMP & ReadAccessM & ~|(R & FirstMatch) ; endmodule From f11f88ac2b78bda02cf83a20d4f28346bdfc565d Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Wed, 29 Nov 2023 16:20:43 -0600 Subject: [PATCH 39/48] Updates to tlb to check access permissions for cbo* --- src/mmu/mmu.sv | 2 +- src/mmu/tlb/tlb.sv | 3 ++- src/mmu/tlb/tlbcontrol.sv | 8 ++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/mmu/mmu.sv b/src/mmu/mmu.sv index f9af52139..e8d87503c 100644 --- a/src/mmu/mmu.sv +++ b/src/mmu/mmu.sv @@ -85,7 +85,7 @@ module mmu import cvw::*; #(parameter cvw_t P, .SATP_MODE(SATP_REGW[P.XLEN-1:P.XLEN-P.SVMODE_BITS]), .SATP_ASID(SATP_REGW[P.ASID_BASE+P.ASID_BITS-1:P.ASID_BASE]), .VAdr(VAdr[P.XLEN-1:0]), .STATUS_MXR, .STATUS_SUM, .STATUS_MPRV, .STATUS_MPP, .ENVCFG_PBMTE, .ENVCFG_HADE, - .PrivilegeModeW, .ReadAccess, .WriteAccess, + .PrivilegeModeW, .ReadAccess, .WriteAccess, .CMOp, .DisableTranslation, .PTE, .PageTypeWriteVal, .TLBWrite, .TLBFlush, .TLBPAdr, .TLBMiss, .TLBHit, .Translate, .TLBPageFault, .UpdateDA, .PBMemoryType); diff --git a/src/mmu/tlb/tlb.sv b/src/mmu/tlb/tlb.sv index 861e721b6..a5f95c70d 100644 --- a/src/mmu/tlb/tlb.sv +++ b/src/mmu/tlb/tlb.sv @@ -62,6 +62,7 @@ module tlb import cvw::*; #(parameter cvw_t P, input logic [1:0] PrivilegeModeW, // Current privilege level of the processeor input logic ReadAccess, input logic WriteAccess, + input logic [3:0] CMOp, input logic DisableTranslation, input logic [P.XLEN-1:0] VAdr, // address input before translation (could be physical or virtual) input logic [P.XLEN-1:0] PTE, // page table entry to write @@ -106,7 +107,7 @@ module tlb import cvw::*; #(parameter cvw_t P, assign VPN = VAdr[P.VPN_BITS+11:12]; tlbcontrol #(P, ITLB) tlbcontrol(.SATP_MODE, .VAdr, .STATUS_MXR, .STATUS_SUM, .STATUS_MPRV, .STATUS_MPP, .ENVCFG_PBMTE, .ENVCFG_HADE, - .PrivilegeModeW, .ReadAccess, .WriteAccess, .DisableTranslation, .TLBFlush, + .PrivilegeModeW, .ReadAccess, .WriteAccess, .CMOp, .DisableTranslation, .TLBFlush, .PTEAccessBits, .CAMHit, .Misaligned, .TLBMiss, .TLBHit, .TLBPageFault, .UpdateDA, .SV39Mode, .Translate, .PTE_N, .PBMemoryType); diff --git a/src/mmu/tlb/tlbcontrol.sv b/src/mmu/tlb/tlbcontrol.sv index 31312f767..dd296b892 100644 --- a/src/mmu/tlb/tlbcontrol.sv +++ b/src/mmu/tlb/tlbcontrol.sv @@ -35,6 +35,7 @@ module tlbcontrol import cvw::*; #(parameter cvw_t P, ITLB = 0) ( input logic ENVCFG_HADE, // HPTW A/D Update enable input logic [1:0] PrivilegeModeW, // Current privilege level of the processeor input logic ReadAccess, WriteAccess, + input logic [3:0] CMOp, input logic DisableTranslation, input logic TLBFlush, // Invalidate all TLB entries input logic [11:0] PTEAccessBits, @@ -67,7 +68,7 @@ module tlbcontrol import cvw::*; #(parameter cvw_t P, ITLB = 0) ( assign Translate = (SATP_MODE != P.NO_TRANSLATE[P.SVMODE_BITS-1:0]) & (EffectivePrivilegeMode != P.M_MODE) & ~DisableTranslation; // Determine whether TLB is being used - assign TLBAccess = ReadAccess | WriteAccess; + assign TLBAccess = ReadAccess | WriteAccess | (|CMOp); // Check that upper bits are legal (all 0s or all 1s) vm64check #(P) vm64check(.SATP_MODE, .VAdr, .SV39Mode, .UpperBitsUnequal); @@ -98,6 +99,7 @@ module tlbcontrol import cvw::*; #(parameter cvw_t P, ITLB = 0) ( assign InvalidAccess = ~PTE_X; end else begin:dtlb // Data TLB fault checking logic InvalidRead, InvalidWrite; + logic InvalidCBOM, InvalidCBOZ; // User mode may only load/store from user mode pages, and supervisor mode // may only access user mode pages when STATUS_SUM is low. @@ -110,7 +112,9 @@ module tlbcontrol import cvw::*; #(parameter cvw_t P, ITLB = 0) ( // Check for write error. Writes are invalid when the page's write bit is // low. assign InvalidWrite = WriteAccess & ~PTE_W; - assign InvalidAccess = InvalidRead | InvalidWrite; + assign InvalidCBOM = (|CMOp[2:0]) & (~PTE_W | (~PTE_R & (~STATUS_MXR | ~PTE_X))); + assign InvalidCBOZ = CMOp[3] & ~PTE_W; + assign InvalidAccess = InvalidRead | InvalidWrite | InvalidCBOM | InvalidCBOZ; assign PreUpdateDA = ~PTE_A | WriteAccess & ~PTE_D; end From ab68a76e77834368d6f7d79ff47f35503584ee02 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Wed, 29 Nov 2023 17:35:26 -0600 Subject: [PATCH 40/48] LineDirty is either the Victim Way or the Flush way dirty, but never the hitway dirty. CBO instructions require hitway dirty. However we cannot mux hitway dirty into LineDirty wihtout creating a combinational loop so we need a separate port. --- src/cache/cache.sv | 9 +++++---- src/cache/cachefsm.sv | 6 ++++-- src/cache/cacheway.sv | 6 ++++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/cache/cache.sv b/src/cache/cache.sv index c527f0eae..80f5559cf 100644 --- a/src/cache/cache.sv +++ b/src/cache/cache.sv @@ -79,8 +79,8 @@ module cache import cvw::*; #(parameter cvw_t P, logic [LINELEN-1:0] ReadDataLineWay [NUMWAYS-1:0]; logic [NUMWAYS-1:0] HitWay, ValidWay; logic CacheHit; - logic [NUMWAYS-1:0] VictimWay, DirtyWay; - logic LineDirty; + logic [NUMWAYS-1:0] VictimWay, DirtyWay, HitWayDirtyWay; + logic LineDirty, HitWayLineDirty; logic [TAGLEN-1:0] TagWay [NUMWAYS-1:0]; logic [TAGLEN-1:0] Tag; logic [SETLEN-1:0] FlushAdr, NextFlushAdr, FlushAdrP1; @@ -116,7 +116,7 @@ module cache import cvw::*; #(parameter cvw_t P, cacheway #(P, PA_BITS, XLEN, NUMLINES, LINELEN, TAGLEN, OFFSETLEN, SETLEN, READ_ONLY_CACHE) CacheWays[NUMWAYS-1:0]( .clk, .reset, .CacheEn, .CacheSet, .PAdr, .LineWriteData, .LineByteMask, .SelWay, .SetValid, .ClearValid, .SetDirty, .ClearDirty, .VictimWay, - .FlushWay, .SelFlush, .ReadDataLineWay, .HitWay, .ValidWay, .DirtyWay, .TagWay, .FlushStage, .InvalidateCache); + .FlushWay, .SelFlush, .ReadDataLineWay, .HitWay, .ValidWay, .DirtyWay, .HitWayDirtyWay, .TagWay, .FlushStage, .InvalidateCache); // Select victim way for associative caches if(NUMWAYS > 1) begin:vict @@ -128,6 +128,7 @@ module cache import cvw::*; #(parameter cvw_t P, assign CacheHit = |HitWay; assign LineDirty = |DirtyWay; + assign HitWayLineDirty = |HitWayDirtyWay; // ReadDataLineWay is a 2d array of cache line len by number of ways. // Need to OR together each way in a bitwise manner. @@ -218,7 +219,7 @@ module cache import cvw::*; #(parameter cvw_t P, cachefsm #(P, READ_ONLY_CACHE) cachefsm(.clk, .reset, .CacheBusRW, .CacheBusAck, .FlushStage, .CacheRW, .Stall, - .CacheHit, .LineDirty, .CacheStall, .CacheCommitted, + .CacheHit, .LineDirty, .HitWayLineDirty, .CacheStall, .CacheCommitted, .CacheMiss, .CacheAccess, .SelAdr, .SelWay, .ClearDirty, .SetDirty, .SetValid, .ClearValid, .SelWriteback, .SelFlush, .FlushAdrCntEn, .FlushWayCntEn, .FlushCntRst, diff --git a/src/cache/cachefsm.sv b/src/cache/cachefsm.sv index b8f2130f7..7136fe331 100644 --- a/src/cache/cachefsm.sv +++ b/src/cache/cachefsm.sv @@ -51,6 +51,7 @@ module cachefsm import cvw::*; #(parameter cvw_t P, // cache internals input logic CacheHit, // Exactly 1 way hits input logic LineDirty, // The selected line and way is dirty + input logic HitWayLineDirty, // The cache hit way is dirty input logic FlushAdrFlag, // On last set of a cache flush input logic FlushWayFlag, // On the last way for any set of a cache flush output logic SelAdr, // [0] SRAM reads from NextAdr, [1] SRAM reads from PAdr @@ -94,7 +95,8 @@ module cachefsm import cvw::*; #(parameter cvw_t P, assign AnyMiss = (CacheRW[0] | CacheRW[1]) & ~CacheHit & ~InvalidateCache; // exclusion-tag: cache AnyMiss assign AnyUpdateHit = (CacheRW[0]) & CacheHit; // exclusion-tag: icache storeAMO1 assign AnyHit = AnyUpdateHit | (CacheRW[1] & CacheHit); // exclusion-tag: icache AnyUpdateHit - assign CMOWritebackHit = (CMOp[1] | CMOp[2]) & CacheHit; // *** why does this not include dirty? + assign CMOWritebackHit = (CMOp[1] | CMOp[2]) & CacheHit & HitWayLineDirty; + //assign CMOWritebackHit = (CMOp[1] | CMOp[2]) & CacheHit; // *** why does this not include dirty? FIXME assign CMOZeroNoEviction = CMOp[3] & ~LineDirty; // (hit or miss) with no writeback store zeros now assign CMOZeroEviction = CMOp[3] & LineDirty; // (hit or miss) with writeback dirty line assign CMOWriteback = CMOWritebackHit | CMOZeroEviction; @@ -130,7 +132,7 @@ module cachefsm import cvw::*; #(parameter cvw_t P, else NextState = STATE_READY; // exclusion-tag-start: icache case STATE_WRITEBACK: if(CacheBusAck & ~(|CMOp[3:1])) NextState = STATE_FETCH; - else if(CacheBusAck) NextState = STATE_READ_HOLD; // *** why not Ready? + else if(CacheBusAck) NextState = STATE_READ_HOLD; // Read_hold lowers CacheStall else NextState = STATE_WRITEBACK; // eviction needs a delay as the bus fsm does not correctly handle sending the write command at the same time as getting back the bus ack. STATE_FLUSH: if(LineDirty) NextState = STATE_FLUSH_WRITEBACK; diff --git a/src/cache/cacheway.sv b/src/cache/cacheway.sv index 382d9ae6d..3f250d69a 100644 --- a/src/cache/cacheway.sv +++ b/src/cache/cacheway.sv @@ -51,7 +51,8 @@ module cacheway import cvw::*; #(parameter cvw_t P, output logic [LINELEN-1:0] ReadDataLineWay,// This way's read data if valid output logic HitWay, // This way hits output logic ValidWay, // This way is valid - output logic DirtyWay, // This way is dirty + output logic HitWayDirtyWay, // The hit way is dirty + output logic DirtyWay , // The selected way is dirty output logic [TAGLEN-1:0] TagWay); // This way's tag if valid localparam WORDSPERLINE = LINELEN/XLEN; @@ -117,7 +118,8 @@ module cacheway import cvw::*; #(parameter cvw_t P, // AND portion of distributed tag multiplexer assign TagWay = SelData ? ReadTag : '0; // AND part of AOMux - assign DirtyWay = SelDirty & Dirty & ValidWay; + assign HitWayDirtyWay = Dirty & ValidWay; + assign DirtyWay = SelDirty & HitWayDirtyWay; assign HitWay = ValidWay & (ReadTag == PAdr[PA_BITS-1:OFFSETLEN+INDEXLEN]); ///////////////////////////////////////////////////////////////////////////////////////////// From 025b04ae8b4d74dccfb3a30091bd2f826f472c70 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Wed, 29 Nov 2023 19:44:59 -0600 Subject: [PATCH 41/48] Minior cleanup. --- src/cache/cachefsm.sv | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cache/cachefsm.sv b/src/cache/cachefsm.sv index 7136fe331..b2a2ebf5a 100644 --- a/src/cache/cachefsm.sv +++ b/src/cache/cachefsm.sv @@ -96,7 +96,6 @@ module cachefsm import cvw::*; #(parameter cvw_t P, assign AnyUpdateHit = (CacheRW[0]) & CacheHit; // exclusion-tag: icache storeAMO1 assign AnyHit = AnyUpdateHit | (CacheRW[1] & CacheHit); // exclusion-tag: icache AnyUpdateHit assign CMOWritebackHit = (CMOp[1] | CMOp[2]) & CacheHit & HitWayLineDirty; - //assign CMOWritebackHit = (CMOp[1] | CMOp[2]) & CacheHit; // *** why does this not include dirty? FIXME assign CMOZeroNoEviction = CMOp[3] & ~LineDirty; // (hit or miss) with no writeback store zeros now assign CMOZeroEviction = CMOp[3] & LineDirty; // (hit or miss) with writeback dirty line assign CMOWriteback = CMOWritebackHit | CMOZeroEviction; From 71066cae12941fa3a314b4941cf3bbc83b57c085 Mon Sep 17 00:00:00 2001 From: Jacob Pease Date: Thu, 30 Nov 2023 17:51:15 -0600 Subject: [PATCH 42/48] Modified FPGA Makefile to override with relative path. FPGA boots now. --- fpga/generator/Makefile | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/fpga/generator/Makefile b/fpga/generator/Makefile index 9c7f557f3..7f70955be 100644 --- a/fpga/generator/Makefile +++ b/fpga/generator/Makefile @@ -6,17 +6,17 @@ dst := IP #export board := vcu118 # vcu108 -#export XILINX_PART := xcvu095-ffva2104-2-e -#export XILINX_BOARD := xilinx.com:vcu108:part0:1.2 -#export board := vcu108 +export XILINX_PART := xcvu095-ffva2104-2-e +export XILINX_BOARD := xilinx.com:vcu108:part0:1.2 +export board := vcu108 # Arty A7 -export XILINX_PART := xc7a100tcsg324-1 -export XILINX_BOARD := digilentinc.com:arty-a7-100:part0:1.1 -export board := ArtyA7 +# export XILINX_PART := xc7a100tcsg324-1 +# export XILINX_BOARD := digilentinc.com:arty-a7-100:part0:1.1 +# export board := ArtyA7 # for Arty A7 and S7 boards -all: FPGA_Arty +all: FPGA_VCU # VCU 108 and VCU 118 boards #all: FPGA_VCU @@ -70,6 +70,7 @@ PreProcessFiles: sed -i "s/PLIC_NUM_SRC = .*/PLIC_NUM_SRC = 32'd53;/g" ../src/CopiedFiles_do_not_add_to_repo/config/config.vh sed -i "s/PLIC_SDC_ID.*/PLIC_SDC_ID = 32'd20;/g" ../src/CopiedFiles_do_not_add_to_repo/config/config.vh sed -i "s/BPRED_SIZE.*/BPRED_SIZE = 32'd12;/g" ../src/CopiedFiles_do_not_add_to_repo/config/config.vh + sed -i "s/$\$readmemh.*/$\$readmemh(\"..\/..\/..\/fpga\/src\/boot.mem\", ROM, 0);/g" ../src/CopiedFiles_do_not_add_to_repo/generic/mem/rom1p1r.sv $(dst)/%.log: %.tcl mkdir -p IP From edd4f902833f01a4244b8b579e660b2d929186eb Mon Sep 17 00:00:00 2001 From: Jacob Pease Date: Fri, 1 Dec 2023 13:21:33 -0600 Subject: [PATCH 43/48] Disassemble target now called with correct environment variables, allowing for downloading, building, disassembling, and installing in one make call. --- linux/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/Makefile b/linux/Makefile index cc19c7f2a..a78b85229 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -30,13 +30,13 @@ OBJDUMPS := $(foreach name, $(OBJDUMPS), $(DIS)/$(name).objdump) .PHONY: all generate disassemble install clean cleanDTB cleanDriver test -all: download Image disassemble install +all: download Image install # disassemble install Image: make -C $(BUILDROOT) --jobs $(MAKE) generate # TODO: Need to find a way to set the PATH for child processes. - # source ../setup.sh; $(MAKE) disassemble + bash -c "source ../setup.sh; $(MAKE) disassemble" install: sudo rm -rf $(RISCV)/$(BUILDROOT) From f99cb03b89afcf3bdfd4941a6b595f769e0e2761 Mon Sep 17 00:00:00 2001 From: Jacob Pease Date: Fri, 1 Dec 2023 15:02:26 -0600 Subject: [PATCH 44/48] Changed Linux makefile to unset LD_LIBRARY_PATH before running buildroot. This allows custom setup.sh scripts to be used per user. --- linux/Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/linux/Makefile b/linux/Makefile index a78b85229..9ef677d55 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -30,13 +30,11 @@ OBJDUMPS := $(foreach name, $(OBJDUMPS), $(DIS)/$(name).objdump) .PHONY: all generate disassemble install clean cleanDTB cleanDriver test -all: download Image install # disassemble install +all: download Image disassemble install Image: - make -C $(BUILDROOT) --jobs + bash -c "unset LD_LIBRARY_PATH; make -C $(BUILDROOT) --jobs;" $(MAKE) generate - # TODO: Need to find a way to set the PATH for child processes. - bash -c "source ../setup.sh; $(MAKE) disassemble" install: sudo rm -rf $(RISCV)/$(BUILDROOT) From 6de6e55acf5caf3533934bfbb6cde7236a1853f5 Mon Sep 17 00:00:00 2001 From: Ross Thompson Date: Sun, 3 Dec 2023 10:38:17 -0600 Subject: [PATCH 45/48] Sarah updated top level figure. --- wallyriscvTopAll.png | Bin 38694 -> 64792 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/wallyriscvTopAll.png b/wallyriscvTopAll.png index 4f675507f68af7b8a86e98a76e3e7b1315de46fb..a0d0e7ccee92d634e81e94744edc4576b3b7521e 100644 GIT binary patch literal 64792 zcmeFZ2|U!@`#&uEk~Jz@1|vIVNfO3B2sLD1${Iq5LTExrGWL{6MzSR&J1w#=WeZu# zzRSMzoX?E9yT8A__x*ad*Z+Av&(rI5-}4%C&bgLzo$I>Jb>8QMp4V2RrevWcARwSt zM=GNT2#7Bc5RfR56N10ca5uJrj|1)~HARA#Eo_tE3$dMomI47mUIf+lWfJh6;u_M( zoqzy70sVL2jui6CZnCT0k(;;n|&;7&$CwW?;R| zM@FiPN=*ivQ_=V8Ic!(E3>x4T8U-0w7wqoRiyw5a8Z4Rj*(%>z-IMEgsoGgx>d?K` z*`MAj-S1nvCG9&`qN@oQ2LE22vxV(gxcIM$R<54nqUkwezP0(z%HlPtWl;l5G?|Hx-_1@9KqR_j!qoYGNxav~)`^$W_*p&Vfew>M_E?i@lc%mC|XRZn;!Z@Ghtcb+iS$kI6S$Yfmjf;WH z`=!SQ@|_A6Xy!>8p6{dLD`tEo^C})Ti~HjgWZ-F?r|Fa4r*%?yFbMU-T+iUSxg6>m zJq3M*b$H=Pe?&uJ62AW z_N{_%ZOjxukgUWwZlLfww2TFQpbIAJfa@@%NOKjQ>rLD5ILVI35Zy@Gm+Z|C!1a+x zQ{$VEKyc;dhW9uvYVncHDeh}Ak_T{ojlMqI2FFKW$gUw?#=_CJW~%#KTi1i>FjaQ5 zs>k)`e8wAB4v|GUNneY<5Uiy_4p+`z#WCb$vb(RMfLPp!dP+2XJo}PmRRF=bP+#@; zRqg4Vh8wUGLv&+*>e)&^mz|F{^ZU5!Gv~V;8W5bk)0Q`0DU|A*Tsi#6F*lw=TPQIW zt#fR>d?wN+q_@+JK0_85huZ+G(KN1H$lr)cOoOz+JCY+wdWVU5eSJH5uwc3%f6n?e zWFsccjcO)E{>GN-p1RNOm+qxhi2c}zuM#NZXLkG1eZ;1YTFTEte9SI6vmK)Vo33%)M+4E%kHsZZ@%m))~ z7$?`J{bkr?bs(kNX$*;Av-^6FXnp=gDkEzDm2j8LR;h-!vuk~5IR!JL&?sS6I7 zC?Q61AM=@OoTBnUHB(@b>Ow1p%tZSJ(G-JN{It!@!SyVe0ZAit2&trtw-o4y^AB!? zbgNOo$rzA7*ZesOIPEAH)i(y=21Cc_{jX`vRti~MmdmYnmKRO5*hJQ$z_D$ns%`GU zr@Px6T$^L!=~atuJuZesu^Z^?mfvTna((+*LN4-PqVRs;^z|F^r>Y!Q9sxaXb$k~! zOhpl?Kk%24U;5UY^|I1&+S8HoRGRVowVBOqAty(@sYsntWY;&FcneO)c9x)!iE}+R z^>P{yXdn|`p)q!V)ys3-vfcxRx&8dHi)65sECm*eU*R-n(s%1iihI)u`wEPuLExyYDXeFL<-NE$~l-wXMJicl`xf4tZun@BvRnrXzFwLPaTEi zg&}#qFJAci=tyM4+fi=i4ju>JT>Zj953qzX9xmn>p6qR+duhWM%s!?me zetHu%{nC)?QEsWYD(C}vx5XooV2G!`hPYs|$i)yJVQP%~p!+PSX|6o9c%k5z%_%$8 z<7n6?Kp%?Aw8HKXjpn~-)EU+ zp3B>DR|PJ@Au009fbS>Z#x%o!giPc}X;c4eQQXbF`b%|iI>mh z*^fKdQz4laYbPL@AYo9&Ns!r0(w4#Qpqlnjmab|8{lq)))$kP|KR4Av?0yE}jI)B> z7b3X%@JWZ5P1Sc2z%<=w&huJ*o8a1%c+PKGWS3cbcx-hgE%Aadl-|4~vR|`WlJ;Mq z<8m%u80J0PVyu~~V54H{xxuvA0(`TeUD#61f+jXjbS_l1zQBatY11u4s=ZS>5ASzo1q7l1)+qRF&^!KV)z z+NVT1w^tbI#*@sF3**cK^0~mD?uEo0>slk@!Mraze2Mkg*;N%^|MP@WE<8Wv6um~8 z)LQ(rNxJVw)ozNUgs?hq7egcy5NFP0*@|)Hnp?6L4o_Txq4jFVEp{`_51+pSe3PT_ zThh+813Z^hZYpL8azR|TK@t!k4VL0A|<49J#g?2yAAu$gyq zF|d)f?e1)?1l&HIZ{5Mgg~^vK^4TUis6rjRaQQ4@#v@`BGdWxbhB=EyEYcCyD|K)& z;J*hV1US(WcMyv-g!PAh{%Mg85Wsz><-}-QA!Xpm(?q~LNYEn`#8JF-$YY8yuMlHp zLfrRYI;4#R%kyuVEKU3r*i8nc4W0}bK#3eqdm#+@lWa!Jm`6&i{IwX{hA+1SDXs@q+;KTlRJfBl zUttex%tN_ZNwLyt2KQFA*5+|U=8wL|#*2A5a>GHfZeP2bH87h;eK?Tb!7pty>Us>R z;*4rEg%lh`=}6EqLkYTNH?&>)vYm^X#wOXfw4a=fez@p3?x_~-lq7E-p|$W?*`F5HlgpTxL74*fgLoC{*zJW9KH7vI2}bsW;_# zv)wrV@Nr@k9h%XfB2MnigSuWFwQ7shjEJnc#?y5O4S8?u%jo-~V}PvzGE~~dOF6eG zMT28iuJk>rI5k~W+(73CyXeKKXF8wflYO&shL_P3_A$;kVZ|{@(l^M)!Y4h=W!?*!hJVGi%kB!?+a5vM5I2Wf-|tUUM_E ze7eYKV6Q*UVrGUzS2$-!PO+OX*1XrquKXCZp1ndX{$(95`cjOT0~u>h&NG*{o@R$B z;u-lgkI*>VejiwDfuB}ey3}2fLq6efbFPc}fuWTpc4)n|5~+fb=r#HGNtf%lGPf-! z9f6!i+XbJ!eB#SL@;UbIt|z*h`0x}i5niA}Hbid?C1-y<>+|j0vVEyLZ$yi82X`EE z5goeY3xc8Ph_S?lZAOuuKn3#v?`^RRH6(Mk&ZQaZk{NUc#?l&DVU{<7sTlLi43EdM6zqj^K z_On>c&Zy2&-d6~g%g(986b(X7(H)b`Ms`g#rdAg%I0YwIx0sNH$v)hg<{_=VLF)`6 zUKX(yLcY}4JG_r1OK+a6F;JC=Og$2E2Vs0_^?PZxD2uR`e*Qe$~!rlSh5zZ(JIjSvTRbmQ!m#hB8|z&8<5@SbuB9X5f5Cg+{8urI{e<8^}G! zZNl)HRyMK0yV+Zm$24HfR6br8Y-ZghAAGs<%>$O^lBup5X0uFW~Q3zrV=j0+XmZw-FS9=S|rb<-ASt625 zPVh?JsJciF$B1>Q1m>ZlviNNtK%%(AVh6F}bw=H(5sD8Too$$(=PoQpR2v6)U!izU z1GhR5=grADX!TiBRQq#<4V^yluS<&1x)G~5`Jzqi<^4+9WJ3Ep;{$J_ENa-~;ujkD ze$G(0cl*StdY|63!>Q7ak{8{OS#-!}8yls|jcN%j=$P;Y8O@LA7`2%#&Z#UA9~F0_ z?oPzo&UD_>ZfZMTXkH<>XPV%9{ZPU{;_GioVv`oISXkZ{@x#|&KXr?c1H6BVM`W%r zHZwE667#ppW64T?&z-ULGypK2qTE=0H13q9`ZY_-@78cL++Ce!LPRZQp;y z`!+hpbgIVfR9y5)3D-l=dO3}m38Jqxyzkm}#6xblhxNvzGbctPt541sTrrNu5Y-zV z=dmDPurXI&aBHc`J*P;6?RJ9}KkB*E{LMS;sFRy=jHpY!I%gKJ|Tu@PUC(QuIwG zOtYO$22%~ML%-&plknz`P^;OyxEFNH5ccs)P>o?z-bd?$MPFiu3t@D~1uxbcXO1Q9 zA%`BW8A;@*zeQUMMdgbIu8XyDucXDp)?VgQoC1QK$En+8V1ZZ$Yq{)8hs>Ld{$OSD ze(_b*+Qp5JZSx40ylrZ?&+pagEDnwywz7PDZ58rMmP%-A(g;HtO!h{|WC7l;g)k@` z3dN?#8`PVRs4bnc!Y>HIaWxQ`i zJTp;U))?9;J~wKlvc8opS6B-)}bZm;x?i)~3cp zhSDG$jmUxQVRA2CB4+*Gt)W&-5loH@TExQY^3ir|E*5U@c-GHu!~LFYw!kz`H=@Dx!r`v>8jnrt zjxHwG`{R+F?ml;$RF@}vY9Db_KK{Rj+6HTENyj2?jPbK>?v`SmIsF~UcCoBCHE}2IAR1sE zo84o{a*r9J8K1e#UW9e}&xH$lak|UZkO!1+WtLN{h+{0Nj{Dj6Pq{j43Mw$5$e5AEGyE%j2s1Nw~aYWxq=%5w7P4->l z1jjyO>R5AA8z{L5?>DAiu;UOe=@ku3WIFlYPa;ULXkQRtZ=b^Z=3K*(UA5ef^M%XJ zJCxYo)`|q4WEqa>6yfL_7O_$(exy-_rb9Bz)pE_<#_k42iy<+7jr77$RElcbj(PIR z)o#Z9I$3rVfy~R8Q-t**5|tlcP9hkF>>L)@SicKx-*WBxw#qikgCs6Ibr%8mtA6 z;St5Tdxpm=PusOur;@k#uL5N1kl?Qq;Jx6j9Kn0bX&EgtV^ zM`)Oc7(LutTFg54D(+H6IF^RE?OvX8{l{%95z({PG?pRPz2=#iW?-Wd}hgA84?KCL&G0FwiFmEh|n@!2s&TRr8+W4YSq!r zph}0N=s36Z<-y7%JghVq!?b+$>_II3#bh5{&B`AuZ9`#cpcq2+@=}Cz)wI5M?C90; z*-Bs*s6O|y9G&)$4+mU3G-3`D+ArOjvg$6@wa|TJ3K_gPEVjE}*q`y#s_*Wm%Oz~9 z*p7L)&&if+_wA>X9+5F3S4&USTbv!6?U-T5ZGo^ainSh7iP)`twbsy z9QFk{la$}p8v^v4OJtuA<7+&7@l>f;Le=A#z1QVl72{R7jN;^-o z|Ky;J;9oTGe?JSTQneLV{eq~My{_pubSgbCQvP$W=^hx`FJ13EB3+>+iVUA*^ z&`+jR=?lmh+OxYYVL2x%hObCuk^Wn+u?+U9Q=~niLoiI>WPh`nTHC*ndA71~MrAC0 zrjHo4Tf3c^@z3QV{)~l<(jNAjR_5eJQQ!LbbO&PiQCp4g`2EmW)$tfkNerd^94m|RzgXbOcK>0%JzdWn zGSFA6EAquZ>#be_>^`c0jHJ()nkw`bi_ePR^!PWC9{Q&7&FgxK5ZWs@e9U_EZ*@9H zz4IbnUz}Ke@3i*?x3%yxz4=-_Y0H2G>Pt(tP8Ku58~$&FFdxleX2TE?i}`g(+}4w=|~E z=?C%_)57_xU118$Y-9{Epa`$P`GE$Wda-SfWd=E1Hq)fjBT0`OJ^E=LR&jlmHA_GD zb1Un;?u`Vp9Fp#*;PgJIl9g|0{{Z7<#gv1u)Ols-kfHj$5jaV>(;v}nLiLI|`OG#6 z^wp}>w+9?E+#Kxtu(|t`*lpKJ`0c53a8e3>tOnlYhU?XL8GQxa4Q zQPvA^B6OM>e$v<-(<=GJe6MLT*ASfPaNJV%KX_y@^0vK)O5Fy}JWGs%e&FM^k17K! zqM?bG%YXEK$w~WiH@9-3*~ED(5IV|fd}0Fr6xCX_eNkF_(kIB&*te{G`CFdo;+!4x z$g3@YAu!PtJ=jPb1#GS_A}=91KTJeIngZM0WiYMwzR?8OrQi>ExJ6&iC#Vwow^98s>7Y(tKsu zM@61JtE<3s==)vF=tMjkDE9JP`|Pe|CW8V;%XM@_>Q64$d1LlUy|xZfUEI<|n`O$+ z`CT>7VYD}JWb{lsGkd7-)*I?ZJ|E#i)J^}|L(&a59GBJQ&FSZa*JO|%P~b%U?os4t z=DTmdY9?Lrb9-c#NsGtKEWVp~t@Dj5iZ>JBF$M+~lpidzvsCo+|G0Ne6n~OQdIUL! z>nL|+PKU%ZL++><1Wu_ZYi^fAhaquaf9z_HyT#1wFU@MB4Faw3$JAoQi2Rll!~^D2 zrjGssuQX@%r*Qz0eupQk6sF2krVun|?Z%n-)LX&EE;lI4x!5#uN4$}jNl=RimA=iU zIcwoa_B-H6W|%~$`w||B!~=GAp3sc)4d&Dr$&i@Si{MY;>s}G^ui$#pX!R#k&Y;S~ z@JJl91n*wnYxEgP(Ez!+rPZ14gKG5Ct$q+19O(!htczLb@hjcNp z#k1Scfz!|=&`|%Q70>jyg6|XXN4d=BVWWwc$;&G`LZxw&1g~pvJwS$Tl^rpD8BD;l{`~-TB#Hs@H5veKYOS+1dMMEqR z;oR(Bru&WQyDA|+1gbj7gQ0ANwCi^D`>x)l_^CEL#HZx-58Q-WEs1jQ16#-glD__d zlazH|(h!7z`(4f-t5KSbGti8R2;(q4;Adw{plE*nbad*iqBt)cQII*~h;m^x zk|0S(nxb%$vTEWWPxawEI$OjdT+#(izq$s;-TJ+2AO~_3&#%?SPR3i2&Tt;UAP}); z5jUxQIP}rl4K|=8lg28@0MzbP~kdgOea`YTPbu;I#x(+n~u= zhNkA36JAz^o3M+rxLI||!kLnj$t2DQolI6BHSHskcAn@%wsdh2nw!@Gc*7^8#j@BP zC3F!jbO^_hm373yvNB{Hae8v-%)}d0CZyt;uTKbbL-g@>h^zxU0-PPLW*0&8`o=CC zun1smb`?02m^B*48NYE9&~cX!q^QqHcx$4N#yBM7tSPE+4Kl=2*}gbf8u-N7FwZoQ z9;`S`u;M=T;ucQtzB#=kjq~)7J+T^ovr8#h>UDF`p z%?CtEEtHQn!wPvC?GCy0tw}>|z{zhsXMVo*4DhMB!TFj_YGAzzxrZ7SqTcjv2s(Vz zVs(zKx2^;DtXK+>vTaI9Q=mZBN9n%#uWb@s`giS(cJmKgo4xp+Tsl5L#?d26WW!WP z?C=zz)ts)F%SBPf+W57;Y;9Qd2k1!GuE|zq71$-m&6&l-eV&ueec|#XB z`MpJh&Mxk{*0C3dsO)T{GPN5&R>t>R8w#- z&XzF>a1c5Y$zlLHS76HdtRt+$&CEHut(hKvuo30u4V%IyuH9gPd|PIbjExRYS;mN~ zw6|S*uU37_>}Ot@%rzQdYH+-%y*~(-md5B#1=Lnvp*9$sIm6wlF3rnKE#RYSgf-%A zmqs)4^M;}HyAX-WUg2;nNijhT*C^KXfv0Ki75$zTn+)STmINkbWd13eC$_UYuafd# zN9VC*bcI|ZL17bPcy)$c89-3TnpusW&rq=if6QiN{wVm2dViTL&ckesY`=s)5I%&Q z=zfn-Tsut4P->7v&nbxc1YAg2;R(#EU+|;6>19Q5Kn-A901&2^Lk|vkk_+Uf(88v`()`3rRljVf!jAx%0OWE=7GM7ssr^ z-}g44VpRlq=|rFG+?CW2ItVw?UCgh75NfBjhX7`{uXLwfhkf^sq^Z$SWNp&cqHLL5 zBj+n48!yn&#*y0KFlkh_mQi}XZO-@8p%B8&*PS-m@HF@Z%r%xYicQVBcxDFzVmdN* z#Q|N)t=IYWXxTWm1)-Nm(5>Ta-X|;yqvLIWxi^RzP{~if6?W9>dXlZ+c=Ii8T^nGm ztC*d*y41b8Uh1;fj4+ypM@cKEzM>z6D;=K6>~xIEoy|s|g7Y3bS6&Ra^zSWp=m81m z)QGy5KCIsUS)x#;($nnbjzf&uIYK%{nfX_3D0p=m3JBFdW@f7~^GtWJ1w{*{Gdkoo z=4&OzWL>z!gq%97nxi$)XKxB6L9@>xyimSqzJP=vhDLsiM>-qA42j+A?|^3#6$z;u zCFSciUob+$r;)1ln^6~(WUfu)JQ93M!Q&+z=Uz-RlwkTV-B3-Rq}(uCjTJj9ebPlp z()qP!IHv!F&@JiU;w6J+dQHriSJ}szUwWp}&**$T-|~HV(M%z(0Q~;7(mr4gNEJ`|p&pHrl1Z$Z&_VFg6K7Rc>uUG1kOZ$L&NobeL|(; z*bF`58+JR;u@I$5qO8i;w_D^i+1^km8sIomW}cVE65U`1ST*=IB6E3_!9}PfuMLQm z!75mBU%@n^%+h=_%&R%f+r*f5n4yX&<=6-1~-$R8rq z@)scpuq_7iu?-xd*CP-iP0dP(P<-4ih|oTUF|q!aL=b@6nhp^1!bxOe3lYi-npbh% zsLQi~)IeMtR|3#++_gaA=RI}%0Sp~Nk27n)m?|r(SvBW``_l5|JzT!~TmU#rm+vMN z=gEUp9G(-fL7dOIXq;oaer7gGW6=o*Yle9biZ!50FGX zT)`2#(ghI;eh#!ycAovw3a--?9466&@0T$YK%KxwVd|@9d0t0OMw#AfdjF$MbH;f^ z563A;qE9(^a3sj}%VPWWg5AEg;5I@`NXtbWcYTn$?Z=JhRTf0_#(|*&18UL2Aw{9x z%)aI>LYgy$8M%lq)I`x5Lv-Y>08=PC4?dt-mAit2ZQ?dSIO~?#?-Du&@ir1V2F)t3 zDnurR_Ynl$1IOMF>JbV=GBN#nMQ-1;A=_3uh@F5qTdchT%_&Ff+OwNS8aPs+RrHPn zkDG)7ZG&^BlJqrR2wOi$G}-NLA-6pf&$8COw=-Yg#l2T}*D827)?$Y>A2sH=7b(dm zvs^muvR$X9zq8}Nt^;W#z*IPi2=?_6I)2EZNAj!9*NR^eV*;Y#4c61Cc z`8$UzDpw!dzn;QndG3iCxTa#+x__I1{^au(Zg5{BZyc;S=(b15QQdKR@Y(H-2bn4> z-_KYIK`1KEK(xVK4=%2n4liZ!xe74~?qkr?TTPwi0-ruFaVbDZNXSW#WAw<75Lp8} z{#?2G;h6|h<86NhY_0PZAaq}Cf*^MMaz>)jMA3qw%o7cN_ z+)4)kh^>~ERb|soRJe|Bqs3RP8#8;frzu7&PwC8`_7-t!sR`)wni6eknh!ZPFxGQr zj5ng;#}()Hu@^!suYaE}*H_vM$n=^+54)F``={QvxXv011;?FSNbur*Hgq+q-zhkp z8Cf8+taGo2sEgQjW-C04_iu9(Q0NC6y;N8WaKSeZ%RG2_E>TzQ(d12Tre&R7vvG;2 zq&vUQHBA=DKCd?6=2=@g z_9vYILCu03HKafWYfxMiONOpN1WD84dm%DVe+g}t#fjv42rg` z4?_#HnJ?As)Ev`Ujm^4A&P*#g%@uV_>+k*HXlpY+X~|utryY~C8((eHJMwLBhcDIN zI@^_Vzt(Cz&NWLplz+wWcauJC=uUXtL$7mEH;cbBQCcwUn2z?VQOv@N<^O1wMXJ*| zSw@8&n)f-6-D8P5rv5kLj-B5!&d5S*#B3k9K6I&>=Kk`V`ud0W4n$uY8dPZT+h(r1 z5|diV`TG)L%kDwb?>(3)MaQhnx;u0%cZ~Hwpg28giOHhwo&#|k!k#=GMcluyp%0x0 zY3mu7PVq+6$S0ptfPIvoI=sUUFQMWNUgk;d{vx2aCxZ;7)Yy?hHu~SOAMRMLd9p;N zf9xE}SMYDbU$RPxdI!`dX(;e{<=GHf^kQv{KGOWSSncxl0VU;sxuJ^8Kaw9nD|@${b^X}n z=Xny}+1coDlD4{NJMG!-vrc|}&dZ!d;u%M-wE+B)=%HzfG;4VHgCxxs@d5dSsw>vb zT1np)&Avt_Oz~6vztc(lv&*z5Z8I6%{WiE8?842(MWgjZO;>ks3GHwF%X257i3tc! zoV$KZON(9J&y<#s;PyKN4EX=*Prwx)AUMcCNI^gVy9>E;f=d`WB#0R&uLB=UKoAg! z05>&5Qh*O9BsdBuW*{Iy9E4&3!CM$72DG!B0M46$fRqBFL8kNawHSQ!vc)=blL#CI9hv z2VztZ3;)%J?qr@I@ToQ(Gh~LY+6}>f3e4lgk5RT_0bYfk^{0-6GNKVII!aJG<-gkT zVp^)@EbVSZvO}Z~{Y-7*UeCl!Kv>=4*v}_|?pLL7`YA!9WkFdHlyt=(`SgPMK7;{;Cq{HuY2I0eUYNFvff{!31YO5J&}^P zJd)N@?>k%-0lx~;f{}y?{c-KNFy9lR6;A;#(5@brs_{q?W?&Xp*p{N*(vI%sbc-)s zYq0WOD}DlUGPPfgJQ$D*S?9V`u;2fUj@4{7FBz4RxYkYpSEdRLb) zzAh;tnmU<+7jw$V_|YR{<09?7LzH;~f~bSr?A#gLJ@=~3%9iPF?=CL4d;9n;sK^pO zt-2QD!lX@M>b6@xAU)rc8hqP@m;rf2WI{_TCOPF`Jzz=6ul9Ih>sI!D7yPezeu; zo*x|GSL+J!>&$=oHDsjxKYyKl$B*{G_YE1T0>6&^-uJg(uQ5V&|Msi!JAMV;Yx&>h ziQB&MEtyAJ-kv|2*Lnv7!@!OL#);F3pS-QVyGMuo@NC3K<=%lxt7Qm%i~0 zk|r`-C*ft?Rf186EzUFX-(7t`za>OYK}c~2oqCJ)n#L!l#H;JHXbG4ABvSA1Y=wW| zR|v)vzWrho5I+AqVJVt)V)pkq!b&MMfUrWv?}UW_VJbY~Aa989((i-|0AXP~VRF=< zU~j%nWB-l89oDJ^*3Cg(UD~rhZXfJIDN^UL;J=c0X zHa>1cmQKcOO@gX^>{7`uquUcc z!!UjLDOUcb1C{E)L~n;3sV!@H?XnttjvOA%t+uTH`f>nR zGZt^0+P^Msm*baD85-dhFcaNb(^P-AC~%3eyTI;shU|~qySy=h2(gmu_OFYK3sh+s zyvY&%UJD=71asLcl9_fCgGrq=qhn{``(?ftBxqrk0c*4MFYJZAxl2?~MM3pCg$zgqR2;l{^yELw5 z5?O2&45@eID$1!PM z`GYkM!XOH2J2t1Bdatkb-3}C#RAVg{w1% zf55x+zd06{*hHw#>p=7e=W^?-HU0+rAGtZc0@Zqr|1_=hw?}Zbdg4EbIB)=eotzB( zcBoEz!Nf&4$0)VvP1unY1RoLF+HE9c;NGdLpOgqT8(*4v`y?fQQ6liB1XlkLcQgFh z=lYsBVQ=zv)zjnEK5GwFlNNF7(`~YCLTgPh*Yu35BD7#~%f-BX+}e+8^KlZNZjgcD z?}vjt0QFkVz%p#QP-z z0{Z>Nu&}+sy`9~v4cb9f<)$_HsvWb6FSlXe_FEbWnQymP+?sV#uD-Q5?C)hUF9{<* z&3g>;VTi*ZpO1?~P#$av4Td}yh6JSUHgF7Kfcp^6HCijcI4zkeuy2122R2WJ0Pu0* zfBz8VO@mXNQyzVK|97r5@}oV4_0^D};?dxsnlu#Mr+ zpd0i46FYX={*5dTCIT)JscWCI-opg}ckOcL{;0(7jsBu$ZAwo8*fo^+jS#78Ee({d z&Gcs#jkl+-(7m8Uk3=};ZlbR?Ee;wCzEa}^&%7YK7gQ?T2MRT54-ArzB(A?FsmKUj z6psN$*;8flw+v2*^;8EpFXn;l+USQ?Kl}4Z;6PD0P%qhkBdd#=cdb5P&&@&Ofj;-B zoukkY^k#}v@PiyiBtBmNf#wh{5Vwth^GD*h5Qt&zF_9Gx;&r*iYm-Kn|F+3)ugms> zLdnt?FbjnCzd|v#UY#1dHFm5^rdeSTf|F6f7r~|cPlpbiw(HF7>>`~+fk`08{^fp; z^Gaf%^7eo|O%!0Z-WV1Mp~z(EZ@Y_ZO)&kjhdjuEd*NlDIIjWx-bzeR6#6xIVrgPc z>3IC0!h0eAt+mw`T656-`cuew1|(VmM$In0m?0bXl#Kf5zHB38ZT5zY=Ct5UUEDZC>gaUqr=x;jY#4r*w zfYUKzC$P=<<*g*7iK9Sy)`bW2l=^QP3`U^nkP|t?46hHMC4QSeIwYvlRm39hoB~^t zznD)HK)*hbepg$ona#N4!@!R_IPCn_&eii?Z82Ol017a!AQS)z##ZH6yg zi*5xEO8rl=>BES&riW554k0}`jh5<;m5JT`Q?uF#1Vc?hHWRXoJAYn6A6dh$EXDmN zsa=_oT{(x392vXMsPSYxB(Y5L3|}0=ljEaH!YQzo|AQbjMD~GRg?t{*6$cKs)ux#S zh^eN_Y5%e51ETt)kL7eZT}W$uAE>Dk+0n4ybC&T#W-R@Ym&pw#>}wgx%`2aa7aAs( zd+7h9-wrA)mj(%ns44&suT@o2jj&`p?|ogh=8uhw4`NN59#_}X!ntpDOc-+TP}!d? z>Xv1|53B$iuXd>@d~F*(&?{ozL6ibqLwzK-w_xup1;{6tc}~uH`I*zAGuP>n_M?X) zIgqP7oLLb~3#pu*qB)-~!72biVc!4B{A~LTy~Q`lRl~vl>&`|aLphIU^At9}ac|ie z8EMi+ub#v_iw+kyS*H6Deyt@m+0Wa)|I7fN*7qE+JNSks(@vJl*<(*Xfe|q)y}rHf znh>!4lq@LhtcrBHX_X(pmHmO`P^m$-wB0vO8_mU z7HR+8@?)jvv|$zNLtzC41&&?Wkr#9{Zxa$6y#JsIn{qHn{KT{I^Z60?7bXWCip~X~ z;dXi6lzc<0lQP+9!RlK_k>9{}5LSG1;n@`1N3f|GDXw(<@^WWuZEv<}rwB@oHqu{N z)zUBvN~Q&i#COt(a2y~o45R}lnM?Or%;uJ*z7ug9c;mw6fFgsDm;*gM5(E|8VtXpW z)fv#J2CA@%vaov*Y3m%ym&0!ocY>H-5>IOmm^Uif<7U1n`6) zG#I0v$cHNC^TIHRim;1OxaCNyTmdBygty-@sKSg6#3Z%|yci)Sj?yH@YT&)f?H9NM zc!Cqw@5!+@In%Tb3u0p^$l>vH$XGm6moMP*2!cy>U{h~PkfvjE7&S!XNgQSJYmiqj zaBsa3oVb=rjH;tUig02C)Nx)YAQhKO9eDc|ayGxw{l@179j$}?1aLNP6lgtlyf@>r z!Mzs)HXXp({56ge7WbSFwEX}su_O?Y3CHUoW*a9Uv||A;b}$fTL;?S#a2kDmce{;< zR;Xg}-Y)M*v^%~1xA=$$9P!>U1qXI?K=q=_MAK(Ygk3tndgWU$9r7Lv`Z{<3tYM>K za))--cXCFG9IFWATb00c)Yp6r+f4q(iYAU?+x?Pb0;(dViyGP_4N)HS_I2?Q=Kj8~ znL@w5nFN&$l?Rm$K5`(zL1*FyjcEYy=pkWDU`FyI(API#g91r+%5Mddk&3-2thqAF z`Y5-wc~;I9kV}IQUDs3AnjkU_tuImHSj zwvE&H4Jtmu{AB|vhoC$&@-1$ciVsVF%>lX+!Y-h3duIvh4fJdK_U8t7--RCH1rd)N z`{tK1oG1->4K@oR92B_yG`7B;7JFF{R;V?MhA%~ym;toT4X6G}5Mx17^YPKsFE|rr zxL^DhWpa0a_zd)Q06w1zy!wz0JZIKLm;p!YAqxPeN3*#Zjy2?h))D;n$$bJKO905i z)Pn7llNGQkxW)i($A<;mj-<2*PxzOnR3J*XKtW4~dHG}jDw!J0nr~@vm%61 zZDJE+&6sG@y*i|SjTw1dHvG>mPhc!N3r?g5hu;+u5^A+^ewS!6Z4g;X36Ivu>u}vAw$B9H@Hhqz#=4_$@9ij zQz9jt9G2g5cW8%-C42#2&{!i=WFz*?p-{ZrJ7JHz%;8Xm`#lsW-tVD^hk)d}zW2{I z!}1=*QT^3W7Q)ziLWP_2477yxGpWl{Miki3t;1Pb#BE~4QTAzwGj7UcZTKBqG#W0d z=$Ng^;X5QF)wlkAhql6nT1{v^%cZ=+V{6U1bYn}0-LI7;Xk(Fw`q<*wR$g4CvO2> zbHWETxbjs5D8mLK986mY?QKOyb*i5Gm8^;7Sje&{{B)Urkpe*4goF~MU{vpwpiv8y z*w~3ja(0WVzZ_dg_ia#5q(FBRza-3H*0U|#%P2jHpG+b(+@=A%%bJruO zKyn+kvX_DA4$f6siY(9_6#I0Jpsg!UZ-uN}-!7V`DnHvlNBubl-wIr3+>e*v&R|At z2B|rm{ z+t(U665_)EbI_N5sxvWHJJ#7p>`O*{If>us#-mZ);5{kFx{=-0`PmB*muD{;n53F| zFD6$l-;>(j^52_Z(YRAehXl`a$SFaPOmvXOUnZ)i2b`wXnzCMBk5cD@f;tQ*jwAmMM!}_e=KV`eH?kY>1iJvw|fb7Cm zZ3=JS=N-kdmDumg_FyZWr$lRi@5*oq6eM6TROSi!Ek(Ga4)cUB!|j{c1enxIM7MN@ zU5YjHy}XB8Hi+!9AH!v#_fl$_+c)(USE*@dI-(h0dF_rYE)%1iaG|NSbgY*)2hWGw zExoVeyw3`OLExr(Dm9i+{f%#-yE&*#+EWr{@NcprGjeq2-tg=m_uN4kUmmV>4`meq zU<&)|7YOU&ozjuSQ6PJQpx<;z$kXF&utN=0HYVI*J!u6u`R3a$+<&hua0od`0UjBg zYi>+Ue|{s~l;%v4m?xtmS*jbvrN?W;GGcR-^1WOZ^Woe>+Hx- zN98c81#&UP+LqJ=>0yHhU_&;cRM@k5Oxr6#@KT;2nvXo083jN~H zW<6(cGsk1POG`(maYH$3&vr~iT75S6LinxaMZxR~W+e_QEGr;b2$5suG+-pEV2uZQ z1=*ifm?_ou-mN|)u*cPA4NgeweW#2+%^YxF*Vvvp?Qrni!~1-M_279p2%8S_za+m+ z`$F*pYjj8>IwW}Pl=416pjSM)sv7!w+rv|3@Q#!{Eax@GEA7hY2alj9?zVoZ=iYb$ zAd{^B@59Kgm9%C&p(qYUG~7Wnfi{-TSm@@SH!}a`io?%G@Vwz@#-q@C zE1zpen!Ymn9Z+IEeh?cI(Vb?hDG`)Barya`+l9U&JY5fE2YIaDA5;Tu5A!X{#JFAwz9%m1$-Y z@key~(nH}HAN+wU8!;!$pd-wP00olIMTkfAV>CRKRw>bl2AkeZGj75R1@LYjE}8=` zuz4_grN6PE{dVj9(AHVPQ zyMEv6`u@v#KA(?y-?!Vnr10<_wlnkh)P%_K3w4GI+q$YlHyu{6uzv#di;K9pA2#R* zIi5S{%KmsqHp25sfg(WK2$p4v#^1;00)_!l5wK7&sS?;Fay%z55tUShMF5C>6|tv$ zMg-JKBy6b~C+~23)<{=-d^8p$Fr{eCI&#OI_1k~c*JwM&{XB2DdJB2Jql8&(j`hfS zkDBK>f`87`Klj1{9VTBdPTMp3$_5=^AZ_6peWoAero-){Ns42lBq5SaV}&xRqwwQ@5sSO z*(h>vN3iPAN^7^p1nug_`B;YfvCfwa3f4FYLvO(ss8Mz^9|%kVbLo?Z@ecXr-`t}Q@NKSbFs-lX(#Xk?=?x*`eV?mWi!08K=N{aJ4vz|Df_rX*8M z9Go8d>^?h+WI*G4wztIXf;)eh6{Nw}qT;U{C8d|n$`Jeeo9dOr26Ota7KY`(V*g_m zI)TJ`m`k23HzOlO{~dMxn=6U?yM5T?96E-!z>eO+s##qmIOD9;LwNg$F%36Uv$bWjmmv0hB{PcW&MQy@+krVsQl`d!!Emt?w1hWf`jrCVNy%k77|?K<_bp+R3{ zs2d6m;42&p^UE%P^LR(a`4VXEJu-DsiK)DT6pbnB>UnwUF~@{TR$p;60w0DhSQwE6 zOkfw|+RwGgzznW51c56zy`5n3B4EP&GWvAkkGslb)`)phZ|nh_+eT;5$BEdRvI^+) zzpBsc*pn%oXEOHucy;j!N2%1e%rI1%%(i4OMH?mErc=GjWmM!6L^J9P1tOvp1x<_T z*bZihs>8tohrK56!`s?c3|@2jZK^vpW{`)Rfil~5SGd1x${@nm3K%4RQoJ~23c_c; zi}Z8sD;|x{j=!HTP~Kg8{gN^|tnA0#Su$0kadAGq*;jCpRJLJuc7iY4eC%BC%!DN3 zmf_FA6q(|0&QTx_OTFKdaP53fbvqYvIp`l>PHF{)m4u1|EVv0a z%fq*^!@?x-kF4Vf-SyR*mLb+YWlSLx=n3|rCiEWt*AKu>8H#XY-qT-u+u!W{w&Zde zbJHL`rriwx?Z?wch~!fTbkl%S&*c1p#n65K^+$5V%FlE-Ba|Pa>c~AI;4Mm0l@!Xf zWY$M!>7{z|u1FFv1xKYuaXXl1WhkIatCI4z!OxkS2lJNATMu!@-d387O} z+O0cKX)3Z;=VjU?YrFv}0HoUXf)v4{A#>xLV+oMFRgAugwb@=qmPRxfh}6Hkeg7~I zs>-%~2S#$bT7=Q@?`@WGS7yt*-g}n=#Y~Pjg&R@OYBEFC7O9_U;x;rY)nZ_$1`7wy z+aIeIu`Q#ili#m)CRC}qh8JC7kG4)tf74^i&Q)W7c@Z+7_aEcW$G!jOnXeFUZSwR zP^S@hg>WkI!R;iKz~LeO?`13bUC+Y9*evx^IpxzRI^{MQ9>e!CDI1|nfPzrUvo-+J zwJ>VKH^RZ>QzaWpC|N$8&!?6+5Tg=?6zhGjqDf{s53%Sd3DybH1@>XQ)EAhY&m6%< zjX~5mVn>7G_sl859-sXd(agoO%Xf|Kz51;j^Zwf# zL;@qSp5H=4uttV|Nyd87L^(Af({tUrFCQd<04o#0E-P3i8XfZJc z`|8q;6AOo?@aZC)dho)V3bqu&hxQ}~j$~EBk--pc6wxmsVS0a+shD@I9C#)XN}x{WG@FyJ^3PJb7?9mK(9|5UUtbUVp|4 z!etVLs&_zAUz2Oo3_&=8OS~=(){T%E zB~;oms0UhCjvLM>$wdIYheJ2}V6X7}FBTTmKDf^;o6N~YZo(-Ldjacme8U<3*IMob zL1vq^XY@;sqFi@8Rj`-rdHPI9)^V;IzCkN~?bZO+g)rdnQDl*&1sZ5SIhlXJ6p3HVus#%w!@IJSEWiC9geCw$%CE$?MPKLr}sS=M4yk zC!Fc=d=Mc{ce<+yK;nb2zz8ktO>kcy9elEz|H%jMsQv(?35HYIRf?qWH-7)s5*;07jL~E zD3Xh$#~DkyqWz%z!_ROjIf>bC1sT7?38gQeo4?;5atLzkh>!{r+SgY@*;rdUDc8Lc zG&xZ?icJ@zLZu1oZO63n%l&$Nacy~V#0>`)i6*@kdVz<~6c#|v&=Rt4YqVjbV{`?~ zE7ebl3-Ux%ZBI6GLK7FwQm{6(AYQj6_xIgs3M1j85#@DoAI^8Wta64SAVKo^IgMx* z+|_=%7+{NnFeTUnA8WRz0ogc&(jErV;S7!wnWvlMQ?Zpv1I)FmN6FIa*v+QE(*VJ? z6P4yFiN4B?!L`& z`VQyfBhK9L1N|g}sfn3$;X^uU4)=DPA%W4U7q=0ZlJn~|u4JLpE*$Z_=v>Q0g)fmz z{>bBZ`RfUEbK%YDcXE?FYh&3fKKZAPn1bM$52oCy@^>WBR(xQDx8Svmr;~Y6RR`%S z)N(^<;KL4n7dH zzC7_MR`R&v$qjLJnElUbkZ-?$Hq2*8(OAHyAM>Se5t_ukd<#+@5!iZULruG6_yRtD zW^<|IlrYyy7=B5%bV1x9zNLUc#PPbr+y$|U(XH-HUqY6`t@2%0EzC~sh?hdsia`}x zUEh0yyGbg0L;vg&+Wsi9d(VwZUULoG-jBUbCqp&CdIDz}k#^2|aDq@lf5_`8E^SBB zJq8?K6!k>YkL;|nvw_j=t_N5hh7KYX>KziV_={S$CC}Pnh(DpyrRas%c+}^vTp#mN zP_Q4WI!8wv<7G%Oy>s&DnGa8Pl@;{W(e{?L{7TNG43_*;c~S*i(IT*vr?u8W{64AZ0Rz=vfi7cY+@+ob{p+m%`eU=pXYj4ln^V5PErKKt42a#P*TP8Rwt{=!~k+|T7 zTLcK$nnP<)V)Il_7$}(b*=AVoAXwbi_~AR$=-U#jc)z0Jj-u1iqZ9aG@A`dej!BlF z0QI&fGxAT@xqdw+lMEu@@!%n%a@JWhwWhb#mf=m;SY0OvGolqbW)~v5;-dX2@OmhE z+*T=9bjRAKC7Xn+AM-hlk`9}Ki0++FeK7#N66l_ROo2){6UTbwz~!c4x0AtcK%j88 z%Er9z%S4ke*2dp~OdyF{ubY450I~7@{-Z>%erB=y=_T-5!#7L(zZ!2JRioODD1CoD z*|YkQbXNT;WxaV20Bdl{!7iQ|a5{wTg9s6D|L^Uh1Pn+iI+1IH4J(z~k-O8Nym z6Tk2ffeK}lGWq-JRf^#1_{18Oa6PorQPpcW;6V1`I$>AcBwT%F;o&(^5L`i|^Y$+t z{cD`(nm}u)e?FOj>uIZsCagbSXW@8L^)Dy>xwPy@_ugx8m*i&YrAX&nQYX)1HmjY# zW{!(#Be8k+0w36EHrht$fNVov4lAR~3jh7D3_`bYkYY3lM+GTLvB^LfBpuzZpjojl zfvvi4pJUZtw)5}}BzOl}%P?N;@UXchumdF!Xro_$IQYL${18eYVn~FOASfzB%y=qO zqsTDTx+gmKkW-3meAs+L@*Xsy7Z?ox1im)e5zfpwpxGP$;xcTJNS^zOyDJ0BD(4gS zR+U^k%AYag{n1sa7luOmbU1J=C`fW)!#4P1HZ@E{h$ese6d`)wctMP7<}mB_j#y_A>P{-E^C9o=Alz~m3MJIJRum*KrK}=FaBO%{P z)UA2Gu2VcvgQ?p8!@o#O_3D?r6!I;Q9;1 zFevN3ExIWN(>&j#pO#s1k3mrQ@2krV(V6V@BDg480#nEzOV0x!+KQJK_5>yN1Ze0% z4MsVe_!$C|v1zV4pKi~OsK7VT_Yq$Vv>?f7m)M>>NUP9$j+1Fya^#;?`C*9;NaCFm z57JV8AJcbB?L4|-7v`qxLDi*Wzjo_8h;fLO+U<@(U%o`LR6*sD%s4F=Z>r9T<$xC= zb9iYQm>E6E$bR=3%i3|xvdq!*#8NK{OG@@v4f~b_9d2TGnFNzuc*b<(D`A;S3lEyU zUw)El5vb!}*jhAwsb~U3;os8=`JR=!^o2cD?^U8K&E8X>1o-~glh@gB-7h<&^cD8g z_(cs~KY4(7pH7Wky>8!!V&NWPu@qBV|6Mv0_mpyObzkSA!;O&?J88&=P*w9D#_bKC z3QL({*#5DQyOJ&IW7|uPuI9_dG`Fsn^9e#mPf@2mr?l591gXDOP zO=kRWy@;I2a$|IWwD@90)8Gs0?f_tSb7sUr1TaXG`|t&;_oj?yGA?#V5D+3T>#7@Z zul%(5V#)Ezp^a%BQcoCLLu(zka1+g5Q73=TiI+NtgnG#te8AHCeXN|Ba6VRA0RzlM zci{te!bFqnVjpMWi?xy9){C5TuaBGzrg=Wf#5@VVLVhEO%?`gZ9qWdI3Ysn~YN zf=gS}bNn`K@6D14jMTHUM3=>juI`|#EHUu5aI~AKy+xg@rm~<$z#u#>epH^fut7cM zV>~?JIe2uOY^F<{)V1|^oD!OVsZJXM3zxWN^zO0*7}DL4_4RIT`B%h2CCYgy7=i<1 zRKabMx;=7I4mQiYhEv+T0c*Lg)%p?#V=gOxK8I30=pCBPin|+f1_}KYyNowx8LJP} zkC4KDN)m+IUvTL2GKeWQM%DP0e~!i5+r!4~eW`AxW?kGj@m2Tkn}#PR*t9CjXug%6 zcay)f8`sB6{8aKM{`SYD(O|_e}gW&Z|gK1EMPmh!6SaB*=4K z27jXdtz}5@cBq)H#sC{9H(bX@>n`1&7fNi$sKzDbXqWRwb_Pwby!?$>dy4ySZ)C;| z`%uk(boj=;_A&8eiz9D}D_@_F?mBplI8pWLzde<<8|N7e{_Ll9w{P!*za|qhN0JTy zzw3|N$%fWeTjqL;pOnciyR715-e|9O8@Ht1@Lc)U+MLJSi&VO>l}>e1xn(y9fCnVX zg7cMKtIx=}dx#|KZ_48L;ykS)tQ^2SyyTCAd+FztPxY$$Lo0`S91 z&H@`wgaAN#_a1~--JYT^;-@WkS(>H_te;z&>Xz^eAa9f@DlGyF4GM;ZY$ZsxQjFuF zu`fBIQbp@WiyR*)aoJ2YzI>u*F#)`a?~=LNUf&4~s|z!&>!unv+!6>zfib12DOBZ+ zRAB5XuOb^tdpBOChXGG>?srzS=vu%gE9@84d)c;tUABr(ZldoKlqVv= z2rg&~XP}KDxv~J;-^slB6qB4~I^1CcQrhE>l%&H!naZ1UTto;g|2~mO%%1`Zzi!=u zQ1iyQ`~TrfpTEN{%B_8N&wZkh@uSP_dP2MPS@#Z_mej-d@suiZk4Ky@CbMGTq5&KJPFtn#NX*+j;c}<7J+)U^`xjCp zth3cNKI%h#i$VE~ef{dPHL7(QZy`oLQ04?YkD!iuZ6$WS2Z;qIq|>Y3#Sv<%U_HeFx`UXm;pmn8X)~72zdzdi^&sSVe}2 z!D{Q2bwlA)BhJ1)Pg zrZH_r4T9~^hF8=g%0Qi($MpD808}i^0|(qXH=Qa!Ef52_nU3XUy_{9F4V9*}?Vza! z_Q(Y9c&ITL9Q(|fI%asx4!&slRZl7afM!CbFV+y|VAjqZN&H{t@>2dnAM!E8Pfku+ z$M{4eBRqI!1&EMcWOTDJ_yG-6^TQdGe=gVFayz1_*>S1o{dn6I%MniNOZn@{ecgenwRBvM|TL$KyLJu`e1T2kZyiit5G9o@YRO~CshF@)EK93&6r~y zGIkc!gO+bNq`zihMf}W$hOf4!a|KWZM`r?-kg{ae=eE}*Ilc=++%5#{kXdQexxbck z83^S+cFRDYTytTvp)h@uhPflBHeR!z2+-Hr*JQM~gjDZ-o|04J7Ac2`NI%5ZF5)L` z=mi!7LjxE6ntVUo8CB`xj1%a~*Oib-;m|Ws0(V~rJ=RrbDpUYqz<7zvi5drYuh!uK z`LcVZ7j|0}y&JNxht3wFzs8K-w2*rSRq6=dFA(>=-jWyP_fF6aRFX0(4eER?H7J8s zdvZ>oQxX%T2~FZqA7u;BfRXI7QkQX)1YtpX9J@&a_?%!@m4MA`?uCvLWl)F8%*853 zoM#C3fb(0d|F7A<69(HCs?0^gX)z&_InSYuZGYDzZX&*6V_upR!G8J?VcRgfvIoIP z220FD0UefOf{tRUY=n;IoE`fJq5d07YR^OD_9*<<0)$WntJ)ll;$PV`kmt=Hs9%U2#w40ef4H`TJs#DAuyEE0&-}Rk#R_%u-N}eB zNbOUMoL5oSCSnz)RY)PYzms}H5iGrLrQ8h{^^{7M3W@t0YyF%FF_aCDpe>`EktAwt z7Z$V>`*Vyp&(7l#;rU9u`wZg<*A=Gc@mlxtAxVb(<&R}(Fd;9@1&&7zs-vTd0hZC=bEoc3m` ziBI>XBVSUiXd{OeL-fGPEnFhAzIIKozQ#Q{sL8zUIM=Z;b@a#jddIIXga*Xzne5B) z<Nb2^Qtuh;UA7qUV`$3P1iasA`42dXb~P%rn>uq$oS;V#eX4E*H6ERiR9E{VT4h zDzck9F)QD~!%e(buD;KiPCLMvx7^-Z`j|6SoDP=;$eNd=<9kp9WqtX4od(Y>7T?xj zIQqYjK=0P`RKb2R2JJZ`E;DD zA7yPhb)}_E>twwunt1#P9zC{vUzSqN^}CGisR77Ogaqd}gV^j{zWVUPpoPLUv6lTQ z?5$1YD6r^}uqX7VMzhseMyz4MhA~3sN-6@09dBQhv2D8jVlEizR$eV_sqfm_G6o4? zZ#aV))KJP)&CN@M0&(dRnn%>Q&5gwGoIr;)Tt@o^)_bdH31+(NZw^HfEDka20^A*P z%8<+XCO)^Cr0wzK*X_oVe6lKxTsT7L*%HUcOLXgnoYB?#tZ@#n7vGv1GXQJq%!x}&7l6pKkq8tyvK^oZt_PEc zugaj>hC>%6fTPB+XN;BfCh=T2;8h8wa7qqnmR87r0uIdsE5lE~4nvJOooi5f8v~9T zx#LtRx3K=J?551C<;c&~&{20ViUZ+g^$)O9>fcg{ zIa}RY_5LxDjte8}RsAi1QwFH-VzhfC3)R2Huw?N{V`q4`;RCU$xgb@@myWi(b=g=O^9!j)_)gl@FqA zq+@!CVG1IAs?uAhW)#p1x}opU)dYmB2tl+Q-S__JyZqvSlfQ?ZTm=TD~nTN;sC?alS@8jh}mPpoIOGsiAj&!wg-Ih4xO!8d`n*UBtd_Dyy8J{nCeJ!#C zg!DO&QFG4Ew8zoYPPW@nRibV;0dhh2EoaJQ=5ZJ6mA#Ep4-b4$b^MX=tfoD%`9%#9 zAaY#&MZ5OQ>%!?@Jj7`xryV_kLIIcXIH&@bT^Z6IfuYR2_zLB*U5y3W~T$!WRM;|pmCivlYT$rR)Ye9%Mg!q_s~^%V z6YD*k?1H^owa9oaVEpl`otVqemRo;yz8&dWEuG^b-Z{I&jph0tTvCF5-JZ;&l`jsH zsfORr&p7$gCf{p9dUd|X?TuKu_Hwy)s$}*;1LqPX?nrwY_=R`JiMzBKaTw(vOhb$r zc5MxxdMzpx@m6u1IPq`SL-KEOdrv$l>OGr@ky{9nOS^D=p)X=Yp7Gbz+Q+z-TLMGv zPqXxBdph06o3)$WU)Z%Ja^%H+I-~ubD%*7J$Vh%jUo_{6&ddq&m2a#!5nFj1hCLLh zlPfDV6XqxKd;Cl~>R; zZ9FU`_4LC2CoXQE$ba$Dc!mmFUnEBfoJLXSTvqo|RisJOML}Z|eSKnv0$w*YF#A4u0GKtU!j=DqH-&kR{Igv zt|LVJELRSJrFPp;m!F}>8BLGfCrD){j}hawTp2?&yA8b{3?IebOm)(f+V7yp1YEZLpm<Y;^eS^_LdrkHP=wF}5ELvV$KR(TQ^k3! zc%87zy#{92paeosI+pph%mor!P~ucV(rFBYCi6zKL;J+yu;@0j8oD#DwaA9b-xeWw z6$oQq|B>&Kp%eG@J+{5bWqbird>!*|9-^SBd_;@M%q?(Tx>(~wu?qEJAvA4AYpN=L zSj9+xbU2L?P<{xKsWWI#u9x{ksPq##zAITJVg5mErr#AK78<7C`sy=*62JJC%{1st zrCr?HugoU}h{?0fv&-LkB9PBUtQY){(<5M0n*MS>M#qww@Y0%}djWCkJR1k43v77? zdzXJZbT#%}1HZW5w_V#@JX7OmqlDI#FX7QC07;!%&ZWpFEg$kY5|! zMoJ$!H7PK`&- zl6>(kwT@%_aZpKXM3anbvtGIcR;?Q1b#9%>pg>ASzkne{4Pn^|^k19%$~mrUBXqd- zQmSBUUvL+alh4S_j67{!PIYU!xqMJD!gc@OT1AofOo8kY z@tgUhgw=6xZNAd+xmkmEA)O0?Uz-`}LHgd^PbAR*#PM}Vd-_Jt58Gzt#b2kXG1YTh zfzac-IMA&`elr;{%V}~;Q=5R{h_VH#z^cnw0l72gJl-?eYyIiXPn&fg5V=IYMKk0m zifgY0wMPms4R+-#YyQk$&F?B6(SK1dJ1=MWj(_?wO+oseCS#Y56c;RwJfww={IwTD z*(0@#SghTcarLYByiXwLljrQ6IVdTvu6#bUV8X0){(tUmg+M2Fk>m3)=BRrj(hnqRe zUS3a`9~CO-5RlrL0-Ol}#a=7#z2<$r4TM6rv2ef36rh)yjF z>@=foJlA9EZPsI7b`Ji8ps-9%{txp>J**snJ9aXVtcq3aQGe_V=}=gVpYO278{Y5Z zugj^O6SZr9Zav;vqPa2~l-M}?$u>cFg_ou(A5A)jV#qZELz0Keff1q){_^OsAnfE; z#1}6n2r^+92+1^PV97V058J74;@X#GT-3{DTzp+et`wkT!KKijiF2(jLHC{#F~Rb# z7>!v07^;vEWx9nRzI(|KPurD@raR&JJ#7WF^%5J-*!ZB@9jscjblZ25cNj$nb^sLi z{JpaGgW@0q_rjEbDH^O3W=>H+1Ipylz~2{}_Yj-`jkm|t<1$Rlwq=lUAJ~WMV`et= zJAt-#XTmjH3--^uri_crR!vE6fr}F9+IL5k4MAE3M`H(P&2&IlrGunA zM6gi!eMz7)B3K8L0gVGPzsjvZjl3x&()v5-acPK*a)l=7I4COp@s`4qvlIKT$(~0y z-{qbP4&q%8Ed*1M^rgHS`aEvalGvcd*pwds4k>V85x@wA0LhR2GXWj6>aKjI|M5pT zp5X@{ti0oEN0Wp7piBs@$LLrX?Zru58~rrmh#TbZ3c)VMl&!p!^0q9B6=rt(>~p61 z9h#V%!HV;C`;}n#a!UW_%IJ7ZjpS)V6}QsKdDc7F?RXTmVy+E(pY&|S#euf42KuS2 zqMu+R`rOr`wve;yuuk{x(_gDJT?**RVb)16*@-!Po;Kc63oW)=>G2(c=%r;^Od5~a zfSQK3_K2%%U_D`av}|7AtW0 zoXMaFdoB)1$~Vedu8s+%Ntfj&=_R$k39WTZ@0i`65S9Y9L&8X?lK1vch!E-}eVB$S zA^cJX)C&pAE(=cX&A2Kq%IRTI_EO^@AUVqxZOb0@9~82eSxs(O5}WIE&yJ6nJ9V6^ z)KQD`7L+Z#eK+xDPWqQ}h~+H#e~Z?G!tdSg?^DKtTzcF@+YFi$eGXF4joPU#5+}08?>I?%L4A0{pY) z476s}QT^x#q6zxT1;B+HSCIhnL2naYy8mNu-sRJb)QirPm2Q3&YlIa}E4}e>9 z2vyMGB#RgNV<1rG$4pNUHGmycg#dD+PCNj2A3#JBi#AN%-=1&V3O(+oNA=J|O2PFT zqj$WT5qI}X4mjW2FkJlp8n8)*BU&NKRvgfcF38*oKjruk1{n0O0p6lPuKKqtU-=;> zJO3-sH23F`=FEh}tDbob9%Htqp93q|wk4nX*H8|pV$)fbr~*AFn#D`4J9J8=uB~Ln zD^VBTgyP}<95Z+&~j9IsTQVYGr7CLmEz~enERAH74ipEa@!Jk`K5) z$CXVzjGhWy{bHJ{EU*yl10X|0fIJR+0D*e$Yj)XWK!KpS0E(#}jC4rm ziX(llW?=?~iB;s&Vb87-Uz=^HG$qJ*JQx*!@>sb=b~hGDu%%`W z0(|Sa^7&HFoyp&k1e}e#*RLS;mek-EtT@?0D|DafLHsx8sm$Oxh`;5|+)&IX9hv$7 ztz(!FxUwS@_$WU?mVG8{l9ytjZRFx|&3HOOjtVIzB!x8BtmphOpByg+HQgnU9n6=h z@v#p$AVBCnNK`X9*bab%+*m}>we;-lR8w&U8@r&fiV_L5oDRXh1y2t_Tka>Ex;Q{6 zuSg7Dztz-)rbKcE@YzUQ6cEvuqQ)FzZYHi@v~9`Eyu@epP>Mt9leBwSgji@G>Ib=J z45DrG_Y-ahK;RX)B~GH!KD{9GyQie7`dd%pp=k5VU8s(N@dCCHIF$@r|4Ua+&f+TOah(>T3HqE94+ieyExFbZ?09IcIMa>2^{6gzF8&Fi>U7bBR ztoA=VodS5VIH*6%WH>Ri5E_LW^c_68Vn zAs_R&Ujr>Bym{^?C5N9|%>j;$X!d({JEwg5^4` z5dmpYxMQ0paUNZ=SSj9Way36%`Ma5pKC(IShc95w`gC@Q5rQb{kEJD)5FUoRsHJ2M|vWY`e0i6d9tkD;V=p@6<60{Is zYHV{f2s{ihEzN(dmnDkY6WRM|3^9@CLW}%KU=z;8fyJht;GNG#-ntgftf~5lRnlbFmnp^YH^tI0mFC=z9RFceRibw{(KF|8q}>+ja4D%J|}A92sBBML~NT$aTSefmS>BdW$A;pK5R z{1&}CQ_-rhw;gDh7C!$kAa-A#`G zbM@bOcy#Ils!ycE{hQV*43zsC0N#HOI27R|U;MRjJP2tyqW62cs%RzVV~vAJE)y}= z!BDyTCG*cB`MdUiBY5VqiN9C3UZ<^J{Dl;ALHn9Bzh`3MNJc${0mgPD^uP-y5)2re zh<|+dCM@gk@1>8{)fdZ&iZ2wMx@Fy~p| z91<>`eE>!%Im054itA40HvXC_pV5SxSUVt30WD#v8?y2^cQ$QHH>68c?3Ho{azQ!4 zL2!6E1-e5HsWCXEO3Sg7XcP}NR*v!=E-3SntTxA0BZxUmtz+Ae^A`>0&*AyVA1%}% z71}M<%7AMI3GrejAkL=FHv5&)>^#|AQp8&DLGuZ4uXB~d=2W~#g{vmdx_rFa^6PNu zAw1iCriWtNOD4m={yfE&w;uu`^h+rSA#t(IjId!+Fj&TcCSG^M4p^u7^x+wdu9zkE^~p?x=m$`}3cNrVKw2@q<89y7SwX$#jFck-S=Y1s zf3GJgd;8_886q&DE;AH+AQV7s?0rYp(|e=GbATPR{n}pK+JLv4_Ux(1X~u-*()ReZ zmUmrw%8w=;Ws0@!{aa03ZXpX;Yg+u|*aaQPw86b><9`~k&_3dIE+;`ufrvf8 zFf?JSTL7{tXD_^G9D7{Nene2SHb;VLGg%c7o|XP768lTw1R0%8MPlUuG(z+~iF9F8 zh1(z6DulYg^iPG`U$Q0y-}kq|?Ju(wg3S9<;r5r$3gL|STj6%seBDr4FSq|&;r5ql z>%R^jSAun0bLT{A9LX+^m&)nIbkB%?I~Smkz1+I?bIHb>87Ux4EEwKr%T6kR+n8P2 z9^BeQS+Qg0w;QAqAKl{q_K{4T!7%o|oF~73ole>KvLmMXZEQ5OWfGA*oDlcq%7D4y z1y-RVExQwlCLvXnGk`OZBt3ho#5@21iBJppccq~8#I$#Rpv>uUgC0_49@hg;Ff45Z zxryoPX#IuP2w%Rj70p)MISm+lI$YfMpX#i~eoEOc7eS8Zr}pJ_y0Zo+rmK9Q%-}Lt zAc7gH5sy9do#&dp{C^DLb{%S{x76A*#PZlr>8q0(qO;$sK%>n>(hpJGx!XDTOP!UO zHh|l@pN`N38~!xCh!H|VJ#L5eZO`MkAz0wJPlo7~iYV8kgey^g4Hx5vXe4XS>ae#`JiN&nd$vgcDq0{h0;Rw0&?!{!B0UY;mgh z2Vd;)?L%a$2knv@RK^(!)Mnz10n23c%64Qf5&3Qf^Dug2a(>yyQBA(geO;n$!-hF(JbpQbWDTdR133 z8Ds036LLn6pi=HC3vM`qC}AyV@%31Vek@?-t2ZB#s1LG+W3l2ud3^U0 zx^VG@iirqG;K6m;(a;*!d!OMxOPlHCU|D|>2e1Z-GXrUs0Y%VoDoDck>q#`r6AOx8 zLf0xFY&m<7*q>NHWrm{;Fp)~@skBx^%R_ApAE_XiBrad=Oa{`j9G6NiGVtKMDDb;_ zcBoM6b^Bm%0CcEp|J zCuV3JQEB@al7W1@JOS$lWr~w;yH>s|BlzI}8^x7nL}&u4))T_LB$8^Z%z?4IhsP?U zELX0c?YD00zW7CCnG%nT6G-|>Y;>Kq$q1nlhb#AYNRu--`|%SnI?0^nB2YO}VSJ{b zfK`2Q;XP@RsJ(z>=kk7H7n`B<#Ij$t`)*(M*i96?$YpfVxI|U*i88v)7?#}o7r0aR zDpigAdDq7SEfekoRGwI%%aM>CP?k|A?Rl>>^PcVtP*4gl`>fUUO@}j9Z$i){6pw9;(=1?}acU zBvvy6rw?njZ$}fi?>mLA*dYK6neC5MpsqqJJec#{=cE3cCO@lX7`Kyk;No-xIUMp` zg2YpT?PrHypvEUqx%lXcv(#!1HDAmV0j5YFxv^T-cAr13NXMUsArK5{G zM)kBB67yb6NU}Y#0|VpEu7kweeR4-0t9;nMVjR>X#YKF$4+U)>VhvvXs0@smOLe3J zcs`WOpW8|3_4!t_SZQcjKA&uFd63_Z7Y05ikKRfye~w_0XlDJ+hsLI^rS60_)8peT7IJ;Z;!jvY^mr>_eRt*2LV1M`4KO|+@-wavCZEB@oPtbXeJNN!eWSF;yxZ zZqvLKtVRMf$N`~#O>+Ug@H$$|cA5u?I3Gq9505r z<=gi&=#?*lE0JCnkkO=223(O{d%3HShT5t2Q9&bKDWcwLZ3EN^MBEqAX(e_u?(60b z{`gsYWNaZ!f0`)rSE~SPPj=CTj>R<7wL99Kp5s>Dkg<10$j^=}PiwC|hY#oo5bF|J zr3Pf&r-{HhpoO$KGB!F6771t@xvj%o0YJ68%3_oV;#ltR`*CzwU&Wq?P7mF+vv7Pj zwER(G#-2OsNf90erj6)OIdG|ubH!p&H%d=3;qD|v@DPn4=maJTa#L?2h(g5Z*iy(R zMGR-ASmALd`%lFVT~jV;YN3Vp;8mxE9vZG2C8HfhbGvW6*<_PIKb0G{xn$OT>a1#S zR7Q;me+{8R@PN69pk}tb79){#WRl{OGN_#)NOr+ZXY&u*2+s=(iSGxsKOE}1bisk3 z{Ux6qh6Gi3rul9GuXJxb;$mC76*yX8!gL7yQ>L;qt9|~i0EWQkVLe9LfYgnsO=17muTh16i2sRNw zXSQ6TL1;JNAsctXbX2{E^2e7lKtxTx8|PS>=Dj8I_KrQ%w4WGY)`!J)*}QlK?%dlG z0VE7^5imAKnVer)V&pl&&{J$cc1}`kU=|wT1eWI60eAt9$F(u&W)&Qkxki1GOfu$d zfx~mIv94@iEL$%{Fslv~s;a+Yj|$sVnEs(SxoqE1F!AJ&NN=u8m+T5bj6J7#i~@g( zFT!(r`^yL%OMEZw>UYizqnY|Ak7w70vt_fWc3+0pN9w$tKZ6C3OdcPGhz*`HYI8i$ zXLZAkT{5vCk%n!W?&7UPY;;uA)AF6BHWAe??AP08fVJ_+xAa%L)NhBxOcf5LJHcX1 zV|q{`R~x|>@w2T{RFJxA^-k%Wi~&@)Uek0uqZtzgzPaf~J$DMp)(FeG3-O{ye~EMM z8Q`^1X(dqbkE}9DOaj83Bx8^)hcaY^NGD^{f3A_Tw;W!x>IPN(w5v5~Y5JiES=dsd z-EvFK8_4BQ6GF+>1sc-#2!zGlIxjQFRv*7&a^zYe6(j=SULpb&L=~i)9{nUk6vXg< z|BVkUjO6pwHRD|<0Z2i^3zL8DI`_dqczeXg!6NAs*#7IOmEzZ)vYpOM2Qb?1_VZ|@ zUFBc9AA?Gxh2v}vv$UYX$0^`j0}F3JFC_5IQJ0{B36v=U0*s_;wa}SN__j_%=rzol z@>^p6LmqPp6B7j$balZ@dzla&B7r}m!~wiA{}$X_!0eVKJAkNQQcXYbN4sryGcc5GL+IN`(tiK;Wcqu) zi8YFnp!=%Jszon?@j#8)A|45K08jO^TmJcLMx1{I#fVLi*@X))@8gm@e*YNio#kfD z`UTloOFCLjvgyoXob zCLdEmQ9nFgi_HbG5@0gvp?V98?gN>IXcDBU752dMaRQTPU($jxZzZ<0liuSWON#VB z`Y#&n_Zfk%QIIEu;v>MLZ90)aHMWI=!3JHjo`h^$Pc$~gQYRn&y=2grYeVHGW7U*{ zsx-yBY`{*?++@Y9pZnSUMCLuUW!oOFow!mJ_@&Q#@If_-3fRCVllFJ#)?{BF{KS9{O`%yWkI>(Wt;`)b(S9S=L#czpz6i7HF;(r_9x#gZtw49Kg*YBEt zd^OO`1Tn#=G0=gpDJB{ib?y5HzP4KtfNRXh{_FIt2HLXi9#&*KpYJzpbsBncTkNL@ ztPRFrW5%QB1V0=b${Rv1n6xDZKrZZi}8^~w09hp(g_okd5{gdU8XcB{S{|6Cz4@b&Ie z3cM_%a!{JrY8DW0+=RYitw4QrG!@$p`d&XK(ft5bu;B52%&h~G`+;THLT5H z82pqA(3C>Mk}ZY61so+$(fGg^d@!XsQ3hOE($T#4hmt35TTfA5WY9)I+@iD;D*sm` zjP7ykt}GjqhzH5HWC3^6g-FhvqR}M%5B0lFx$qa3-Nf8isoJ}^jlEkH?FUi)YEYPt zV*mL)7GO(7%!WRcK=w7Wg;R%!wF<|XIwECn&Q9uMWAhPdDPrFxN_ zo_xpNd`NPOd(h@u9r^v+&yQ}G4*ce5=)Jy(>+y*h_{>LFqY;v|MhNZ1Zg)Ccg%wfB z!g3PGNT*d3Otc%ez-$bR?7D1oae0p=pXmm&>hcod<(HeHr#_Jhn8-666a7qghbs9W*N-Bj+fa7eM?iR$p2ciGx8x(}+u5K^ybeH*r zE>mE*1c?kZPLs8OjPId2Rw{(hmrGcj0)nJ(lfGk>M^4GY;=x&5CsKJGi30zYVG;Ib^c6$rKf{ULGu{#_f4k-WtKKrlXj~|C4h6{NrAyy6Q zw=OES7wOXypzF;yXb^PK+$8U@(bND{uyYNtD9KXnSwRIeHCPasmW=s4olw+ha_~2H z>``{V&?Z;E0fz@Gc_2SGPQWscEY){3y-Dx6R|y9S`14=Nm)K)|f`tS=*^up%ra94r z;}nWfha&0w*7&miGp5OE zHs1umh2cdEi9enq9EbKD$zQB~gc}Y;QKJNo)eq8EJSZbqFnF24B;I$+)sh$7jDLNg z0xP0;@kGnD0|L4KB=J<`=yO{&UHyV5bzeI!B|R4xt$Me!Sx+|4@S8Q%iR(T=8DgO+efbmg-lS(=(PA z5s?kujTF+1rn$Xm{p$g5=x>Au@EIvYu8p#;2^X^IrsBAc{p+G{wmsw1T{139Qzt{B z;;CPso*P}pUo+y73A$vhgtp6d{1&0mrHk{a=ENeK!!zYa`l}<}bDqEHYa7nYJP_Ly zZGba0Owsu8t`=YK33*zS@Q?kp>j?afl?AbdBo;1(`mRIjNnZE*w2(B1M782NMt)=c zNcB*|JJ|cU))D(8^K9ML0Jdq02a~@c_#4*(Vg(5BU&WP#sRv$xdwbfhDv;II7v$HY zz^tKxJ~3la=B5b6=64tzeVm=$mxsQhh;Ad02S3!J_$^}CZ)F!>0Vk#7DL5%`-+&k4 z`H^hHJZETMgb;KF!qaciLBb$T;!{GS0eJ0y38f0XD%U06y}YUxE@CW~@A)dP4?t44 z2WGIh*P;iChU;m7u3qURG_)(ZfkkP9`~^0G4LQtk9lYHwux7y2OH!Dx8NoG!$!th_ z@_5)IEG*K7X8Hw@?JYn!upyZVpjEcOsvr!!TfRf9ZE3)r-+_|qj31cI#N1K05)>4Sg&^spZamR zxYJ+4&}R4laxX^@wOxJN`{9s+y;AJKeCV}DY52;^142J$K1nUuVP@SvN8N zKRlcNu&+3Ie}w4u#(n2OT#$`=z)il!|FJ&@)oHa>QTA%_iI>qwc4!hF zxm;V{d4Yp{<}#RT8{WF2JK_1|&-|o3I%Q2@_6qgX_tf=`Ajv9dbaP_?Fs~F4MyYpv zss51#-t^3Xlda`_g9Jo(KtutIeDMD%?#tt$ZvVY24Mn(R%{s}xx7f3c#2{pB*(po5 zB>P?@A&foiC?g?RhwMrAwaG5rStCpKY%|X_RCo9N`#tA5uXE1xI`dbV`F^j@wY@*@ z&vJdP>aw;uy897vo0V>v$WytMv~n#@;*i1_(J(#ZG9=-VhI zs5<-yyeVjcGz}1q{{?T}AZ6(>IFbAl-g>A(8VSfR`w4Fk)8W9E1(G>`!rQtc!5tR! z3*Me_pN2mMNwdG;O$H>Eya$WxC%k9(6a{iX-IR0=AI9}?(DB5;Iu z{08v*F|r=n>Xc!Y!4TKje|7gGInqOoj($adVP+TPNq?~_4-{xX-V8lZ7ldngsJRos zecj-eWLA110Z57V+dKG?aoAvVMA~o5vA(3_sh=Fgg9FY$wG;Mk6 z)JvK3=1CSP<*)^6?-&qq1KSm7AdvW)1Zt!^p6KWY7MWXf0^#Fo=R4F$I^(Yv$?anb#oCkMDudh-d;xKotaqdbX25 zT_0*yptqn8XwnA4RxirS%*<}+DcY8+hCFb6RxbVc_Op0OG{`T#KC-q?Ny`?LDL(-} z`KKfnKHtZ8L)fxIe4fNM2vmzC-1`TvlNv3y3TJ$Jb+U+qPzmSoS)qRH<&$?Zv)Dk5DRP{&2ib zWL`n7p>QWSIs_`n9)>q+Rt1Fc^g=VpD~6*O8}&n>A7?T z^1}@Pa|bCR$*Jwab;kdE-)YLd$=HCs-e_>EEAMJ+5K7+z5RijM>Oq}(Gw~aEoZMb^ zz&8)PzP))a%5Ymo*PjHOh9@|;3Bj@3qR1Fl=pzEL(;Itn!q zWDaso4qTY1W8i4_6IR`~pA=f>Y#vrzAq#Jhjmq&>2AKkh0zw^~%op@K-lZBEDm0Oj zg$K^2+>q@7$5%kA47F(1U2w&v%n6Vcf6(FyX(YQfMN+gPbR(8f{L`+pQSa0PbnEX= z$U{M1#lf!`;LwvzlA!Q{atDUa=sdW!E2*7?ur$H6J%0hp6oR@v2O7KKB*!ElI31~c z;=b<<*-?2y^6mvKZPMe2(?9y4NyLkbeshX6n{8k0QM$RJYyXXnNZ=qBf*x75*aqlXdRmgP)1tK0!*vyOwZat>RU-q|5EI`_`e{sY;k^y-9z5V#G{~m8uM9 z3s}LwM(n|HEV$FLS`TDJ%{%|R&pLx#fdbvw7%#kmP_*gRU2b`y1a+*?Ccy?p85b*Y z=2O9~$2mgf+6T{+FviC!PtT^xWtmT@5hS4XdC%4Ea*a5MYt5Q!#*bc8aF9|g*iJLQ zX`c(_G$SxWr`jP?+mpc#76d4L2Xed8*2n=hNk0uHm-4d6a4$F*L~tzhViqo1UGd(r z1wOvFALZMcB(w!Y{S^C{hPlg$MUTz>S>#k;SW^m;d{S?C4lu@--h(#oDg|)xyGwFs zGtbc791xt)PC&Uhq&J~g>iOGypAMV$7-V)xKbC2EA|ED~v7&rzGO+@|hCQ)1G!HvB z5zZZvtC_#LpWs>h;qI*ruX=}4Lu<=t-*~IQ)rEw#J8%5ub)G{Pwc9_McxX|DOshSZ%%)?;aW`hzzt{S z-_;by>w(!L6V`fuVkk<$C2kvei;`;}#&~7XCSDes8n8z9+rwVP&-V4AvH~G-(;mS1^ zrK^J<<4`3l-Kh|9E}NZKS~EnEGReKlwx_Q7$n#!oPYw9oF`?sU7r!iiD<9p}%q($p z`#F_P zTXQbO3^#4oFYc6tcwm{%FdMyq`Ysq7{`?&P^~bquIlgs$$80aL<6<5+C@*vU=m|-{ zHxj$9^t%K7W*7h71XroNVUzdA%jZjSJuImOO9ee}Z6FJ|_YEQIv$L*GUUjU?lz|rH z!(Y`&)12PTXN0#4aN%hXiudezC;?QE)CGZsh$`KDorBvC_^!#e`%$ih*qWDm+25hI znsAKmhNiJxo5cK4vI7V@VoYrbZPcWL*t_;g7l+arW(nK!I5i5}wn$iXH5)20+B<_e zldQTx$!@dL(ck8MDW~D$>~nYpJ~f}+MknTAo$47<@WS~cZmMWXv3uMDSwy+{;CLjS zlU%3|Oq2K9L*M}O#0W2nK6jH^r{)|}_pzMyq?M$d|k zOSg|q#HL*2+cL+7%wEn|kCrsLnnnI%UONvAXTlY_AxdgKpqvRvN_hi{1kP>{Cp9Nh z5$RUG6-#*e__0>0HPh`*pZ(@L3r>TPC?;VPb2IUKI+blnqcsLiyVt=h)whE5YpBqhbxjCj7b-mH*lz8|0q=)g`GPH47+(Un#rzqRcGQJ?V% z&U9*^od3~2lBiWmmqk+ttiozCjYf*yRlRKCI%! z(|+;N3=P?%(;m?)*Cw}8r1>HMF0Jlc%Z$fOYlnST2m)zx2cSy?E^7t41O&Jj!l8#45n=>W!pE_LmGgx&B-S`8@^Cb7#}1?hW<5E4_VhBV>UXR3`OVxB_?_tbRpSjye(K~r?jrZo;tc92dic2o z;k)gg@?g>;$Y1OcCQY(i+->_y{fyL^h<3vV*Fn_t@6COuf!EZJfUVs8&u%~Zo#Gq{ z$dM{v>FGSz68$0IDA(ss@kC2y=eZahH@E>^CkR-=QxCQdidmY2Q>yYH>DBJ5CbTtu z8ywdo922PK+{k>A$yx|>`AKeYmwb$9P3fI99Jf3@4T75r?sa8~i58X5%Js;st$Fj? zd3T>Yzf`kjiqWoJZR5|1R$UGE_~f>`&LUb3Zftm2MNLc#!jFemHJ>ao^#P$GBwhc; zeg$*bte7|MLyqx+N`4|rU8nl=6rq?lZn zZzl#AEXSuc&>m7jd8m3Ou;CdE#QBa3cP=J z7Zw6BXjzSZEr2LUUMq(&kX4UMz<1e$3^4e^FZa%mFn%jnxEL*KWUhaJI z{E`_yxAvpVMf>b3D2pAhY9>@26?mKzV#`d5Wu)QA$bjajS;H9fX|lACFEn9+F@cvT zAhzVBr>|c}_IEvpf?5Y`OclR<8%a)wKE><~J5~l?3>FwQpg~iF=!9W;;2*Q0FclbM za|>in6So&YZze<;qJxyhg^q`r9T{ZB&=C#Ch!0Nj&A(3OD}9=i1p5rzwI0FKKl7 zNztK&ai2d9^F=SVq4u^Z!|Ry|0_j;+ot~MxDH5Ctje$iwUq$ooaSaCUa~OwM%WyfA9#45%nC{OuqO2NhNH zhtu7ex{aX{*OAFI9Oe%HN%2PNY0)A2-84Ri(-So{i_0dD@ps%d*ON-EGSEe6_Uf&*C^T@nI&m4L`%eSxKgE|c{!1Vp@vRnvfq||IgTW(?W>$Hm zSdV~jAJ`Ug>v;uubeXXw(^~%W-k0!z@0a$<5B`f0_urc?*jul|HB#EQQ}X`){v7Sk zN$F`4=Y4iA2|ai==HnzArXXbeSPeMD5lHX87&KJ0tj@=0%hkA?+dUWQCByXDI2HufD3VL_!-B~?!~IWy!Rwb2OfD+hhj|X zSd~At#yX!2u}Fs4rjM+=ooOR4!$9{phs@QI+^2u+f$C6>k#rGm`k?KK z*#KQ5H#lJdd3GU?!#wco2xQYUXav{~#i4;5l2?CiB`yOb>&zCF^uK(egySU7E|B>& zj=Q}dL;Npamt{b8RU-pAia+zfW3+$myIb>+oUyw!9MR;;u$a{6zu=2n5N^|zE?eTV zv3Yay>c4&;yT?h+qJ>lx1JnB3j&*Q`NtL-|)|Rz+`^bI*EveIRwDOW-*Le?mROASm z%VeUTj0l_}@=?LcW$57izY$vgxyKJ7GBTeZ?op5yQ9kUlGf`7TNoUVCR#O&>JDRmg zj0yeMx4J>*d?<4Ru9US8w5k`(8|AImewy@;c`Ua)}cMVjISk^@wO|wrm=aV%11pzRLX(2hX z|3c?_T8_D}K#n2M$ZvxQ5D@$Midk1Bsh+DNs;3D2?72sLvx$@bXT#>S=!7w6XRH0V1?H0VDu z12ZKy--TZ-&VAe#AC=oI8cLG}n*j!6RF;@dyDaJR8A2a7_0}NW;i06YP9`_Hds@b;Q<8~0W@w#_fNbKESb$vKFB*#b=0?u-S60B=@SwF_ax7R=S zGAU`b1d|<(MzeiE-~&FY?Eze*41~ZvI@f z$}|CP&Ob%@;Pf%NoqLP;T?6J$5>?pzivuDNLSQ1H4m|KPvl~u(bM&?AhOAzbDFJ#_ z;5_edT!oqR^w4$Wz0St&fFcPN%X6PHhy5J>((Hg?@W3svz}iIF@b8^yvx6KS`sh?e zb^h8CzuKvo0Vr4UAHE_y(ABSfHs|hMBY>9GjK9lSp#;m#w^M=*pE%?cf3-}CaMZm> zidFiBnuOI%ig>Msw98A@LW(K;=Gw5`(1!V5SKL4!KSQP##9fbGdKVP^* zhgRNlGL@_=|4a*+ljnht9^w&LKF^@h9+F7w0&e=#?hfu7RwDyV`$JF{~Tq>mOvfdQdFe(C3+9Ujmo^RHg#1} zg!Ml=`bq%qgPlk4^I-I}{j_-uy!`PO-JAj4us=Y$tE^wHuk>(t_2+xrlyz}g}X zF%~RG^~()ph*#WEH&y2Jk?-=uC%ey@5#N~93vs}g94QKte zpjJA7vo;&>TPp&G{MnmY2ybs6zlX6UJn+x1e@HcLk~W)tqJr>J{u4_9z5-BZ{U@&Yu4>~e|3j_fLvijS**qfIFaBESM?1LH}OTKjs9cMq|C$SpKb$I`$jx-!e=KVZy=NoLP z(jH4R+!N3}m*uHzDhDbovfF>N#943VZcT~Yd4Eo!`;Kn+0z^U{;+Lo21(T7+ToVZ* z#WvtK>RV&|n)kd9j80WRKK=1q0die!%tAonztTnqF4u0AW7^fDBW$k2DsjvBE6klC zFFdQ_;|}#(z+Q8}o_*b>z4OhPKX~Dmx6%UF!F{`)Br04_pDjbvq}=!-=dS!=%C++8 z+JSCW)Yyw5E-updU{iohFi1eyi*`~j7bBV~@Pdvnf@6q|7fI7hq?NG?{0t2SBY1GbHE%K!Kr}zXQ<`BsbW@T1dS^26Ut)_IGSMGNl4b_M}0_A1a0; zt7ss@FP!^#&34}pT>paWH)^0Gv+hc~XwkLtd4@CisZ&yD|u+pT0 zO5{GSDE!044M3{n^{uO_ zO0qR4!PI_Do2EsF+UUXqWhGy#fI^Y-fXy ziJT8^_8r?DR=@XYt?oB!ds6o0Za%m&!6tpV? zO0xK~Xl<=vc8*HXKZxBAc}oX9`R{>Bm5cllnx&iy(aCt?M=7Iq51xV9GJF^#!)P=QTOpHMh^% z+?85$S6aYf95>kLGwxI9W8u7ZQCfUcXy0D_Mb%)bSV(`0>+s^^duiU z#uA*XCMd?p+ntu*b2>s^;JdCYjcKsHy=5b8Tt&Y*?k#Tg(E!O;fA{uz%RrC{hJ(ED(bBgQd-RN@ze1>y#cdp)A9SM zW4o3nH^wmdvR6WI7fe)RUnzcbv5EVeU6)x-Vn!b%CzG9*z=(mfIhq>iL1qLj*YCIx zuUNQ;8sA8{U&pKbR*vbj+o#pW4d%^|!$#WhRod_%hbmz~rtE{s=^qTR-=RgzL~Y`b zS_toVa=CJ5y}fVL2l97l&{_ZJ(p^#nhPWfv_36bbAE!|CNV(b9@{O=5y>Ow@sWrE= zHB~*Ul*^*E6WB8CkvG-aMDC`eSGxDhZRBCeVt<>{E4=WcV{QeDcaE)<>P63FFGw-A zOF?oP_FAfztt;2c0(+lr7p{*6`25XyJ?Nr?3Czy7?e#0R&hEJlL3n)BYqt|41w0}u z0VBd9MDDC*Y_)jKzZuDj8i39e`9XM1{8vm@w<{CgiTJ}*-TiqWIaYYJ=f9i52k0|- z9dxsMA#2-3;wwEGO|xihQk_}+TqDlsw$b3qErksMnOrZdG=_)J+y!6!;=i`$ECI>E z*_M}$cWj;lhD5e^GOB=V&IDO$ufl0kXC<`nHm34mu5JzadS8F+2?A;O(?@wI0Fo4e z+&hX&D{J4e;f2rTD)<;N^aLB?bv~HMR68#&%-uS0*A?NnBJJ3c)bO9QH!@vY&u)x+ z!QJp7dmhNL`+wWspJ_oo4I}n$9c1ouUui(Ckfl6q-gz%PNOOG3Ku8euqqZMs^m8N> zxgc|Z^RG;yN+)eqxHk9p-C?mOB+if3i{Su0qYB$@qJuexs zs$KU>{UE;0q^vn5F8w4hz#v(ePP7@vxf7b+-?E_>>Tq|r2HvVeJ7nAhsX$c;eBBBa zQwtNBw*^NEw2|0ygJCDu3x&|m5|2R@Ny(BesyAhP7x33iCMj5ZyRPt2ny;u-7g}xi zCk6J#5DeCC-=*1E+5K>WGaPu80IP}CgdIBq&liCAVU#&XA+{aNoq?Bl4y-9aXd>^x zDr+tV)O{J=6X(}io@uC>(^T5{9x)b@d==)s(nx=$dC>Ip_i*Xw&}kTW(WZ zuX3UJxl;Rcs1@)m1ra#K98bXeJJU`{dHDV3>Qlh{xpq0(_`ucF&G3^hDev1LSwB^I zJI}iAFwqBgnN^D1!+jjVFM=pny%TDG^z!K?wKvL`-S1C+(xhG4*tQf3T;5!0Bp`op1$_I`o0`1}$0SxYtp#9~!+niaJ+wo%Vd;$j#9C-}r+mZV` zsKE8jk&;BSbUb}JC|*@UMsK}bLytc>@^xi$sHU68p(^xYfNB6my z@QArKG&J~``^5W&!6LA>O1L#A6d3qYy&HH)B|U@)G$U8*{Ewye!wXCBENUOdA}BD( zZ}`IWuLF}mNXf=Txbx+Z2joVb2I;A92A>jQax(%4+#Gl6Q+a`E$u-t9)QukHxm2# z_(EXJDT(&ji0j`$<#1WKx_x)!cfC{ENKm~kJsPaQBft^@|Nbxj^8*%0-CXw_6|i=T zitvecye1${Mh$Msw!RHJYUCIMOJP&+YMT!Pbp$T!p z$B*HULL052gm4=8Tp+yXAP~sT9%FH{=nR|?Py`K}q7LtrCq(|h$MWe72Vw*vf~cs- z(eQ7Yh|Wv}kw50}u7$Duf=)qkp{8B$g~6db%6a>%iX0_Bp12p62_%4*{b+uQM;Wvg z?eL?a7A+87VrL)vA$4@-LBy|E{fB(Yk{f}pTboP4_gaU@29_|G7a`)W7gJ3g=t>=u z7QB*FIlq>4Z(C2_5gywe3tGLci5jYic1^D;|5(##Lg=6}-;W_+p4K77_J!L_;(7%+Gf9pDIzM>mh3SHE3!nkBssLywFLBJSI@o*lFBIqKk=*(adS$TU3hts># z3QwoE>04xoWk2`D&2>yjqZt1t9ig3V?q3fWj!XD#xNBWx)w64Q{`~Tw3=4g*!f83Y z`_`t0@uhN4rfg1+0^PA^VKw%HvP>k#UnN_|0xd)0r@^?lXu*H=Jia!%R$FI#a0F*0 zW7Uv@`+mJR7P!r@UTs*>yQp{~(`x%t_;CHaJ^!c*7}8d`Up;koO-TqW<&YsoY(%8# zN}mE-Gnq4c7hdVa&(2=)SJ%)Nd)3sMbJ;1EW#4#A4r^>1EzcN|Ar?JE1bDU9UVXAz zChc|a@DERW+!C$&OD^E?t{HD;(ZA$Y=R{p97K_{UeGwSvdqnf^LYxwSW6U1hb)KDH z^zmSGjZYX}j?2S6{b>4VvE-{g!2U7tdr8EvWs#k!bxB`v#M8^xU^Vj);wD7W?XNKY~53fFS7cy9CeKvii&i<-e@x#NJFZ$ zLLbZeNsUphkVml3tfOZ*QsL3!Ma!$OT+D?462esfHFIYnIS7q}Az@Yw<$xd+X~%uP z{-|g9P~CAwLydoQjt{;ztnJ74;_jy1D(>2z=qW!kY{SvrS1f%ofi(9fZ`vzM`D_eN zyaYQjdhgd-?|K_Zv-;;)@WpB2c3D5?t)^;Q4kmgz)!sVj#`x^z8d5~d=txy7oA&Uy zjh7Bs4{+T7_^$o`4Gal%=zzUOx&3e4b$<}rI)tYlECCMh0@e9n$HV_3jQ*D&yL8sc zBz?bYdA()h&AycEQg?+o{z~`2rLm0ehsbnlqq6Sl>R*=$j6!5 zND8}o>*Qoej#rpdQYkGuKVEinBi3Ou|6L@xcX~!5c=F)!jhcJ9NKazFkRj8ur3nN~ zik*gIwFiN;({$Z=QO{0_-BdNYzTss&l<<)}eMOr!$%L8@o+RDtoG=8Mbh{kH#<5({ z>@Z?`13XGG=_;X4BIGtzy@*VA=JgCEG?)iI;xM!xUpq*zVz!|sCoFDkJ@Ake zG1Jdv!&WVgKU3w|FrI`*?pO6OmHP+Tr4M^>ZTjm8?`!l;f88*FV1i!xZV2MHap!JM zi{z0%HSD}0IfjrS=~Ec=8nmczX3$y|V$s4xgcl8HA&FTesl~s$-q;u0!;iDsQopB~aX;^9>Ab>Dc8aaAsC4X?+uTe`Vo zv>|w>wDR`PEEN9Bjm2+Nu8r7B`uP@br|WND*IDkpX{g55-A|5fwi_a1az9NhU!u*j z@E(D}VCxHgJ9HlD6-}?^HOp$-@1A+K(;3X!TpSm8=`6;%UwP+(TCBeWraSzs-^o72 zZmPXEa>IiuY+cqQ1OdpIGhZ-v1uS~ z>_;pJ;X-e^g>;NeQ!Q=1wXS@zGgh!Zr-oWzJ(XhI8PaUA@;0H{eCKw##<2(zu?3%+ zz+MjtT~tQ;z{B?RedNrMFOrzDRI1#dl8V`Qu$0e8QMo=zK{oanY~TDcrU^%>OFldOg zUK*A*y^qoDmH;zk^bE|m<0@w{W48*u;irOo91AkL(+wEU*Re($6@2`gsgV&AN+i-n0=x6^91R$s*&t%~ zmI>$E=~P%oy^66f>fpqk?BpxvBZNvVApL+!N) zZD0K8Y|JuoO?CSem!`dmRArcrOOL0|BN(klLilt%3^Q0;x%MsWadV8nkDKtyr_0~s z-|2n%&T5%FTv09NIhG%p)F$#p&Y_=mF@oOwYwmhq(G1lE3(T^TAkF|)`|fgTWa&D` z?x|Rp3O@$hi0}_fYrBPBRh52lACKHx9xMrWUj3&{odn8vV=?O%0!uzSw_PuFyXy9Q z(mY$EJ6*&gQPf+z^E|A#t7YY7jC6W>6@5x@oHW}7Og|~YTW6z0-iZ|Zu(!#;p00bn z7-q&4K-O-0YR=!TAn$H$d*W=+1(VdJY`wOD-E^UI{HXfKY~HlOglqj;Bc4oRFB#4F zCp4vRkTw&!Sb`ntXYE$R^}SN@tqedEZ6r_7(r2n zAFc3An9aj(3nuY}L_BomZZD_IIW;qr$SUDPG6#Ph(b1QPix4haOfLA^m`cZ1G)1)} zo9K4A5QZ|!Y7ZgLp7*yaxxHp-B9znRpHC9Y+S-;QT7!F~vv0!yVDcgQilqVFWHB>& zM0VXL&Ej0QJ;?N`f4=pQ|1pRy|GIY*6L%eK(y=FF{^{{qM(&wNDh9NDcn!HPs~X`do#7tmW>iXo|oq6EB+#+!3+)>xG^j{6y!_JMZ0O3j2iPWW(n2 z)2(hAl%W(V%Dd)@+_XYSdc($6|iTE1q2~E=YqP5_mvRMNR~^ z+RCu`790f$d2W2O_DF`$?u@O^7yjVR+AyTvXh@H)QIg?dJ{Fh?*&UaaFpsM2KVpN+f7#cuu>#52{5$=-_{8`yfW6!M<#4IF|e|{fImDSYVPnEe_P0pypAC+AO z+uEFq?zUABGDK6h5Ttlo;kLVs{~s3FmxuxBq4z&Xb2^MP0WW|_WW^5isIhel@dw6^cSwB zy>i1Jl5XDqP0&_(SleJvD1W@EJ57!mFU1+L5SicvvX5GJ=CkZ95sf6R$&)V5(fSl+ zjaK75(MSimd_aanw;ymeCymd3C7r&KjJ{_Ri-^OS)(=m9*ng{$2i4sQ*xniIrt<6Y z7r~qz$lo-7v8cQ38!HloBeB412GNm%4k zup5^a{h)8|ZNHJbO2O#VK&OFvdnXBc_W_I4gHuczm@^n}hmBY_BS#@Lk^jn7BV{d%+FU72fAegN zem@kTMBL=76?R)KnG_*h9L)KF&-Tt9bzY9^OWG1g<{KFMwtb^M|BL%K1D7OPESkF? zp+63_#f>vEUm7E($aTHP3=XRZO5ZC}l(N?2kfhds4c|-(N+T8h~ z{-P&KyZk{~aDV%oVSyS?u67HQsTl?1$=17$rgr7v?!C2Ru7lMkHQMb>)@yDi=|QX7 zY3vHg-S84c(SnUCw!!r|_yB9~djwNndsoYv@CPYfJvlRa#FBqY?=Uk8_VpbOC|X`+ zmActtvD{=8Gl$N^p_|07uG!?P%(O)YF?;Vhyl=B8fjbtn0=da^XS|*#tzR0t@N}g3 zO>4$E8~#he=mhKY*xc+9&oW2p0XhKLL~M(5G{fhCvORMfIDV=6ym9Kvy&2JW??P<9 zRHw08dQlnc2JlY%3u>h_8;*T>*=2aPQ@s)hdsdwwZ1KHGWaFlvm`3w8d=3UfhN*qC zqmAT3GfUNQ2S>1QoKYq;>lRd|UcU+9CbG^Z7Nq2}d#6Fd?8%rb>0&B@-VAt? zCki>#F*shX@Kb(wln!m)0_6RS4ezs?vtzZew(BOd6rA@2zQfoPMCbqacpmU zNp3Iqv_QT2&R2gaid6$*_Do*Xi?R;9)CSIIDt{-cJYzP)@FnGkyCSWqw%4|iv)k#h zYxaxlm%p?my{U)r)_k+Oc71NlE_)W&VBK?DShk>eaCWa@-GVY7lH>T&xXF;58P@&o5F6n60AU3eq zccMS}2Ax?AgT`8tqjR1}i74f5r}QUG3w|GNqF8QKb@x`f`f>?pJ@YdH{zmgPDOJWN zAanePc>}gw+HlPGJ{7@R<)E~4w&*D*N<8QT!B}%pS|eGh@q6EN@Lf~YoYSHjZj-_R l`QLVL{nIWhv=Q6Iv%E|=_AM=A09c7fRIX|$zE&^~{6EJ$#k~Lk literal 38694 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 GF8>P&(2TVJ From 3bef2a2361623882acc11674a2aa62ec0dab4e01 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Sun, 3 Dec 2023 15:49:06 -0600 Subject: [PATCH 46/48] Better name for cache signals. --- src/cache/cache.sv | 10 +++++----- src/cache/cachefsm.sv | 4 ++-- src/cache/cacheway.sv | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cache/cache.sv b/src/cache/cache.sv index 80f5559cf..12a81028f 100644 --- a/src/cache/cache.sv +++ b/src/cache/cache.sv @@ -79,8 +79,8 @@ module cache import cvw::*; #(parameter cvw_t P, logic [LINELEN-1:0] ReadDataLineWay [NUMWAYS-1:0]; logic [NUMWAYS-1:0] HitWay, ValidWay; logic CacheHit; - logic [NUMWAYS-1:0] VictimWay, DirtyWay, HitWayDirtyWay; - logic LineDirty, HitWayLineDirty; + logic [NUMWAYS-1:0] VictimWay, DirtyWay, HitDirtyWay; + logic LineDirty, HitLineDirty; logic [TAGLEN-1:0] TagWay [NUMWAYS-1:0]; logic [TAGLEN-1:0] Tag; logic [SETLEN-1:0] FlushAdr, NextFlushAdr, FlushAdrP1; @@ -116,7 +116,7 @@ module cache import cvw::*; #(parameter cvw_t P, cacheway #(P, PA_BITS, XLEN, NUMLINES, LINELEN, TAGLEN, OFFSETLEN, SETLEN, READ_ONLY_CACHE) CacheWays[NUMWAYS-1:0]( .clk, .reset, .CacheEn, .CacheSet, .PAdr, .LineWriteData, .LineByteMask, .SelWay, .SetValid, .ClearValid, .SetDirty, .ClearDirty, .VictimWay, - .FlushWay, .SelFlush, .ReadDataLineWay, .HitWay, .ValidWay, .DirtyWay, .HitWayDirtyWay, .TagWay, .FlushStage, .InvalidateCache); + .FlushWay, .SelFlush, .ReadDataLineWay, .HitWay, .ValidWay, .DirtyWay, .HitDirtyWay, .TagWay, .FlushStage, .InvalidateCache); // Select victim way for associative caches if(NUMWAYS > 1) begin:vict @@ -128,7 +128,7 @@ module cache import cvw::*; #(parameter cvw_t P, assign CacheHit = |HitWay; assign LineDirty = |DirtyWay; - assign HitWayLineDirty = |HitWayDirtyWay; + assign HitLineDirty = |HitDirtyWay; // ReadDataLineWay is a 2d array of cache line len by number of ways. // Need to OR together each way in a bitwise manner. @@ -219,7 +219,7 @@ module cache import cvw::*; #(parameter cvw_t P, cachefsm #(P, READ_ONLY_CACHE) cachefsm(.clk, .reset, .CacheBusRW, .CacheBusAck, .FlushStage, .CacheRW, .Stall, - .CacheHit, .LineDirty, .HitWayLineDirty, .CacheStall, .CacheCommitted, + .CacheHit, .LineDirty, .HitLineDirty, .CacheStall, .CacheCommitted, .CacheMiss, .CacheAccess, .SelAdr, .SelWay, .ClearDirty, .SetDirty, .SetValid, .ClearValid, .SelWriteback, .SelFlush, .FlushAdrCntEn, .FlushWayCntEn, .FlushCntRst, diff --git a/src/cache/cachefsm.sv b/src/cache/cachefsm.sv index b2a2ebf5a..9428bf5b1 100644 --- a/src/cache/cachefsm.sv +++ b/src/cache/cachefsm.sv @@ -51,7 +51,7 @@ module cachefsm import cvw::*; #(parameter cvw_t P, // cache internals input logic CacheHit, // Exactly 1 way hits input logic LineDirty, // The selected line and way is dirty - input logic HitWayLineDirty, // The cache hit way is dirty + input logic HitLineDirty, // The cache hit way is dirty input logic FlushAdrFlag, // On last set of a cache flush input logic FlushWayFlag, // On the last way for any set of a cache flush output logic SelAdr, // [0] SRAM reads from NextAdr, [1] SRAM reads from PAdr @@ -95,7 +95,7 @@ module cachefsm import cvw::*; #(parameter cvw_t P, assign AnyMiss = (CacheRW[0] | CacheRW[1]) & ~CacheHit & ~InvalidateCache; // exclusion-tag: cache AnyMiss assign AnyUpdateHit = (CacheRW[0]) & CacheHit; // exclusion-tag: icache storeAMO1 assign AnyHit = AnyUpdateHit | (CacheRW[1] & CacheHit); // exclusion-tag: icache AnyUpdateHit - assign CMOWritebackHit = (CMOp[1] | CMOp[2]) & CacheHit & HitWayLineDirty; + assign CMOWritebackHit = (CMOp[1] | CMOp[2]) & CacheHit & HitLineDirty; assign CMOZeroNoEviction = CMOp[3] & ~LineDirty; // (hit or miss) with no writeback store zeros now assign CMOZeroEviction = CMOp[3] & LineDirty; // (hit or miss) with writeback dirty line assign CMOWriteback = CMOWritebackHit | CMOZeroEviction; diff --git a/src/cache/cacheway.sv b/src/cache/cacheway.sv index 3f250d69a..82956fc29 100644 --- a/src/cache/cacheway.sv +++ b/src/cache/cacheway.sv @@ -51,7 +51,7 @@ module cacheway import cvw::*; #(parameter cvw_t P, output logic [LINELEN-1:0] ReadDataLineWay,// This way's read data if valid output logic HitWay, // This way hits output logic ValidWay, // This way is valid - output logic HitWayDirtyWay, // The hit way is dirty + output logic HitDirtyWay, // The hit way is dirty output logic DirtyWay , // The selected way is dirty output logic [TAGLEN-1:0] TagWay); // This way's tag if valid @@ -118,8 +118,8 @@ module cacheway import cvw::*; #(parameter cvw_t P, // AND portion of distributed tag multiplexer assign TagWay = SelData ? ReadTag : '0; // AND part of AOMux - assign HitWayDirtyWay = Dirty & ValidWay; - assign DirtyWay = SelDirty & HitWayDirtyWay; + assign HitDirtyWay = Dirty & ValidWay; + assign DirtyWay = SelDirty & HitDirtyWay; assign HitWay = ValidWay & (ReadTag == PAdr[PA_BITS-1:OFFSETLEN+INDEXLEN]); ///////////////////////////////////////////////////////////////////////////////////////////// From 1ebc7aa95aba507c89bb1a22399e0412fc121fb0 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Sun, 3 Dec 2023 16:43:55 -0600 Subject: [PATCH 47/48] Optimized align. --- src/lsu/align.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lsu/align.sv b/src/lsu/align.sv index c618bb605..d3ca5ecc5 100644 --- a/src/lsu/align.sv +++ b/src/lsu/align.sv @@ -146,7 +146,7 @@ module align import cvw::*; #(parameter cvw_t P) ( // shifter (4:1 mux for 32 bit, 8:1 mux for 64 bit) // 8 * is for shifting by bytes not bits - assign ShiftAmount = MisalignedM & ~SelHPTW ? {AccessByteOffsetM, 3'b0} : '0; // AND gate + assign ShiftAmount = SelHPTW ? '0 : {AccessByteOffsetM, 3'b0}; // AND gate assign ReadDataWordSpillShiftedM = ReadDataWordSpillAllM >> ShiftAmount; assign DCacheReadDataWordSpillM = ReadDataWordSpillShiftedM[P.LLEN-1:0]; From 9348025727c3c2070e6f06116527649a0f291cd7 Mon Sep 17 00:00:00 2001 From: Rose Thompson Date: Sun, 3 Dec 2023 18:19:00 -0600 Subject: [PATCH 48/48] Cachefsm simplifications. --- src/cache/cachefsm.sv | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/cache/cachefsm.sv b/src/cache/cachefsm.sv index 9428bf5b1..7c48e65dc 100644 --- a/src/cache/cachefsm.sv +++ b/src/cache/cachefsm.sv @@ -74,10 +74,8 @@ module cachefsm import cvw::*; #(parameter cvw_t P, logic AnyUpdateHit, AnyHit; logic AnyMiss; logic FlushFlag; - logic CMOWritebackHit; logic CMOWriteback; logic CMOZeroNoEviction; - logic CMOZeroEviction; typedef enum logic [3:0]{STATE_READY, // hit states // miss states @@ -95,10 +93,8 @@ module cachefsm import cvw::*; #(parameter cvw_t P, assign AnyMiss = (CacheRW[0] | CacheRW[1]) & ~CacheHit & ~InvalidateCache; // exclusion-tag: cache AnyMiss assign AnyUpdateHit = (CacheRW[0]) & CacheHit; // exclusion-tag: icache storeAMO1 assign AnyHit = AnyUpdateHit | (CacheRW[1] & CacheHit); // exclusion-tag: icache AnyUpdateHit - assign CMOWritebackHit = (CMOp[1] | CMOp[2]) & CacheHit & HitLineDirty; assign CMOZeroNoEviction = CMOp[3] & ~LineDirty; // (hit or miss) with no writeback store zeros now - assign CMOZeroEviction = CMOp[3] & LineDirty; // (hit or miss) with writeback dirty line - assign CMOWriteback = CMOWritebackHit | CMOZeroEviction; + assign CMOWriteback = ((CMOp[1] | CMOp[2]) & CacheHit & HitLineDirty) | CMOp[3] & LineDirty; assign FlushFlag = FlushAdrFlag & FlushWayFlag;