#!/usr/bin/env bash # Checks what the memory controller charges for moving memory. # # EVERY OTHER TEST HERE IS BLIND TO THIS. run.sh strips the cycle count out of every # recorded result on purpose, because a number that moves whenever anything changes turns # real differences into a crowd of meaningless ones. The Test Manual says the other half of # that bargain out loud: anything that genuinely wants to measure cycles has to say so in a # test of its own. This is that test. # # What it pins is the RATE rather than a total. Each case runs twice, from programs whose # instructions are identical except for the byte written to the Command port: once asking # for the transfer, and once asking for GuardOff, which lowers a fence that was never raised # and costs nothing beyond the port write. The difference between the two runs is therefore # the transfer and nothing else - no instruction count, no setup, no startup. # # Written by Anachronaut set -u ROOT="$(cd "$(dirname "$0")/.." && pwd)" BUILD="$ROOT/Tests/build/cycles" ASM="$ROOT/Assembler" EMU="$ROOT/SplitBit" for tool in "$ASM" "$EMU"; do [ -x "$tool" ] || { echo "$(basename "$tool") is not built."; exit 1; } done rm -rf "$BUILD"; mkdir -p "$BUILD" PASS=0 FAIL=0 FAILED_NAMES=() GREEN=$'\033[32m'; RED=$'\033[31m'; RESET=$'\033[0m' [ -t 1 ] || { GREEN=""; RED=""; RESET=""; } # A program that sets the controller up and then writes one byte to the Command port. # The eight registers are written the same way every time, so two programs built from this # differ by exactly one immediate. program() { # program cat < "$BUILD/$name.asm" "$ASM" "$BUILD/$name.asm" -o "$BUILD/$name.bin" >/dev/null 2>&1 || { echo " could not assemble $name"; return 1; } "$EMU" --fast "$BUILD/$name.bin" 2>&1 | grep -oE 'after [0-9]+ cycles' | grep -oE '[0-9]+' } # The cost of one transfer: the same program asking for it, less the same program asking # for GuardOff instead. 0x01 is Blit, 0x02 is Fill, 0x11 is GuardOff. charged() { # charged local name="$1" command="$2"; shift 2 local withIt withoutIt withIt="$(cycles "$name" "$@" "$command")" || return 1 withoutIt="$(cycles "$name-idle" "$@" 0x11)" || return 1 [ -n "$withIt" ] && [ -n "$withoutIt" ] || { echo " no cycle count for $name"; return 1; } echo $((withIt - withoutIt)) } check() { # check local name="$1" expected="$2"; shift 2 local got got="$(charged "$name" "$@")" || { FAIL=$((FAIL + 1)); FAILED_NAMES+=("$name") printf " [%sFAIL%s] %-34s could not measure it\n" "$RED" "$RESET" "$name" return } if [ "$got" = "$expected" ]; then PASS=$((PASS + 1)) printf " [%sok %s] %-34s %s cycles\n" "$GREEN" "$RESET" "$name" "$got" else FAIL=$((FAIL + 1)); FAILED_NAMES+=("$name") printf " [%sFAIL%s] %-34s %s cycles, expected %s\n" "$RED" "$RESET" "$name" "$got" "$expected" fi } echo "Checking what the memory controller charges." # ---- Between two banks ---- # # Data Memory to Program Memory, well above where the program itself sits and well below # the vector table. 256 bytes: 128 words and the cycle the pipeline takes to fill. check "256 bytes between banks" 129 0x01 0d1 0x10 0x00 0d0 0x80 0x00 0x01 0x00 # The same move with an odd source. Nothing about it can be paired, so it runs at the byte # a cycle the machine had before the path was widened. check "and again from an odd address" 257 0x01 0d1 0x10 0x01 0d0 0x80 0x00 0x01 0x00 # An odd destination is just as disqualifying, and so is an odd length: all three have to # line up or none of it does. check "an odd destination is the same" 257 0x01 0d1 0x10 0x00 0d0 0x80 0x01 0x01 0x00 check "so is an odd length" 256 0x01 0d1 0x10 0x00 0d0 0x80 0x00 0x00 0xFF # ---- Within one bank ---- # # One memory cannot overlap its own read and its own write, so it costs twice what the same # move between two banks costs - widened or not. check "256 bytes within one bank" 257 0x01 0d1 0x10 0x00 0d1 0x20 0x00 0x01 0x00 check "and misaligned, within one" 513 0x01 0d1 0x10 0x01 0d1 0x20 0x00 0x01 0x00 # ---- Filling ---- # # Nothing to read, so it goes at the between-banks rate whatever the banks are. SourceLow # carries the byte rather than an address, which is why only the destination and the length # decide whether it can be paired. check "256 bytes filled" 129 0x02 0d0 0x00 0xAA 0d1 0x30 0x00 0x01 0x00 check "and filled at an odd address" 257 0x02 0d0 0x00 0xAA 0d1 0x30 0x01 0x01 0x00 echo if [ "$FAIL" -eq 0 ]; then echo "All $PASS controller cost checks passed." exit 0 fi echo "$PASS passed, $FAIL failed: ${FAILED_NAMES[*]}" exit 1