|
Nugget
Bare-metal libraries and examples for the original PlayStation
|
#include "common/hardware/counters.h"#include "common/syscalls/syscalls.h"#include "exotic/cester.h"
Macros | |
| #define | PCSX_TESTS 0 |
| #define | CESTER_MAYBE_TEST CESTER_TEST |
| #define | CESTER_NO_SIGNAL |
| #define | CESTER_NO_TIME |
| #define | EXIT_SUCCESS 0 |
| #define | EXIT_FAILURE 1 |
| #define | BIU_CONFIG_ADDR 0xfffe0130u |
| #define | BIU_EXPECTED 0x0001e988u |
| #define | N_READS 256 |
| #define | N_LOADS 32 |
| #define | REP4(x) x x x x |
| #define | REP16(x) REP4(x) REP4(x) REP4(x) REP4(x) |
| #define | REP32(x) REP16(x) REP16(x) |
| #define | REP64(x) REP16(x) REP16(x) REP16(x) REP16(x) |
| #define | REP128(x) REP64(x) REP64(x) |
| #define | REP256(x) REP64(x) REP64(x) REP64(x) REP64(x) |
| #define | ADDR_SCRATCH 0x1f800000u /* scratchpad SRAM (fast on-chip, no bus) */ |
| #define | ADDR_ISTAT 0xbf801070u /* on-die MMIO: interrupt controller I_STAT */ |
| #define | ADDR_DMA 0xbf8010a0u /* on-die MMIO: DMA channel 2 MADR */ |
| #define | ADDR_RAM_C 0x80100000u /* main RAM, cached mirror (KSEG0) */ |
| #define | ADDR_RAM_U 0xa0100000u /* main RAM, uncached mirror (KSEG1) */ |
| #define | ADDR_BIOS 0xbfc00000u /* BIOS ROM (KSEG1) */ |
| #define | MAKE_SPACED(name, seq) |
| #define | MAKE_GAP(name, op) |
| #define | MAKE_RAW(name, seq) |
| #define | SW16 |
| #define | MAKE_PRIME(name, primers, tail) |
| #define | PRIME0 "" |
| #define | PRIME1 PRIME0 "sw $0, 0(%0)\n" |
| #define | PRIME2 PRIME1 "sw $0, 4(%0)\n" |
| #define | PRIME3 PRIME2 "sw $0, 8(%0)\n" |
| #define | PRIME4 PRIME3 "sw $0, 12(%0)\n" |
| #define | PRIME5 PRIME4 "sw $0, 16(%0)\n" |
| #define | PRIME6 PRIME5 "sw $0, 20(%0)\n" |
| #define | BENCH(ret, fn, p) |
Functions | |
| CESTER_BODY (static int s_interruptsWereEnabled;static __attribute__((always_inline)) uint32_t timed_read(volatile void *p) { register uint32_t sink;uint16_t before, after;before=COUNTERS[2].value;__asm__ volatile(REP256("lw %0, 0(%1)\n") :"=&r"(sink) :"r"(p) :"memory");after=COUNTERS[2].value;(void) sink;return(uint16_t)(after - before);} static __attribute__((always_inline)) uint32_t timed_nop(volatile void *p) { register uint32_t sink;uint16_t before, after;before=COUNTERS[2].value;__asm__ volatile(REP256("nop\n") :"=&r"(sink) :"r"(p) :"memory");after=COUNTERS[2].value;(void) sink;return(uint16_t)(after - before);} static __attribute__((always_inline)) uint32_t timed_pair(volatile void *a, volatile void *b) { register uint32_t sink;uint16_t before, after;before=COUNTERS[2].value;__asm__ volatile(REP128("lw %0, 0(%1)\nlw %0, 0(%2)\n") :"=&r"(sink) :"r"(a), "r"(b) :"memory");after=COUNTERS[2].value;(void) sink;return(uint16_t)(after - before);} static __attribute__((always_inline)) uint32_t timed_write(volatile void *p) { uint16_t before, after;register volatile char *q=(volatile char *) p;before=COUNTERS[2].value;__asm__ volatile(REP256("sw $0, 0(%0)\naddiu %0, %0, 4\n") :"+r"(q) ::"memory");after=COUNTERS[2].value;return(uint16_t)(after - before);} static __attribute__((always_inline)) uint32_t timed_walk(volatile void *p) { uint16_t before, after;register volatile char *q=(volatile char *) p;before=COUNTERS[2].value;__asm__ volatile(REP256("nop\naddiu %0, %0, 4\n") :"+r"(q) ::"memory");after=COUNTERS[2].value;return(uint16_t)(after - before);} static __attribute__((always_inline)) uint32_t timed_burst(volatile void *p) { uint16_t before, after;before=COUNTERS[2].value;__asm__ volatile(REP16(SW16) ::"r"(p) :"memory");after=COUNTERS[2].value;return(uint16_t)(after - before);} typedef uint32_t(*)(volatile void *, volatile void *) PrimeFn;static void primeRow(int n, PrimeFn sync, PrimeFn base, volatile void *c, volatile void *u) { uint32_t bs=0xffffu, bb=0xffffu;for(int i=0;i< 8;i++) { uint32_t x=sync(c, u);uint32_t y=base(c, u);if(x< bs) bs=x;if(y< bb) bb=y;} ramsyscall_printf(" PRIME n=%d sync=%u base=%u delta=%d\n", n, bs, bb,(int) bs -(int) bb);} MAKE_RAW(raw_same, "sw $0, 0(%1)\nlw %0, 0(%1)\nnop\naddiu %0, %0, 1\n") MAKE_RAW(raw_other, "sw $0, 0(%1)\nlw %0, 0(%2)\nnop\naddiu %0, %0, 1\n") #define BENCH(ret, fn, p) static void report(const char *name, uint32_t raw, uint32_t base) { uint32_t abs_cc=(raw *100u+N_READS/2)/N_READS;uint32_t marg_cc=((raw - base) *100u+N_READS/2)/N_READS;ramsyscall_printf(" %s raw256=%u abs=%u.%02u marginal=%u.%02u cyc/read\n", name, raw, abs_cc/100u, abs_cc % 100u, marg_cc/100u, marg_cc % 100u);}) | |
| cester_assert_true (d0<=(uint32_t) N_READS/8u) | |
| CESTER_MAYBE_TEST (loadCostByGap, load_tests, volatile void *A=(volatile void *) ADDR_RAM_U;ramsyscall_printf("=== load cost by inter-access gap (32 reps, one uncached RAM address) ===\n");ramsyscall_printf(" Format: GAP d=<delay loop count> load=<ticks> nop=<ticks> delta=<ticks per 32 loads>\n");for(uint32_t d=1;d<=48;d++) { uint32_t bl=0xffffu, bn=0xffffu;for(int i=0;i< 8;i++) { uint32_t x=gap_load(A, d);uint32_t y=gap_nop(A, d);if(x< bl) bl=x;if(y< bn) bn=y;} ramsyscall_printf(" GAP d=%u load=%u nop=%u delta=%u\n", d, bl, bn, bl > bn ? bl - bn :0u);}) CESTER_MAYBE_TEST(storeCostByMirror | |
| BENCH (base, timed_walk,(volatile void *) ADDR_RAM_C) | |
| BENCH (w_c, timed_write,(volatile void *) ADDR_RAM_C) | |
| BENCH (w_u, timed_write,(volatile void *) ADDR_RAM_U) | |
| BENCH (nb, timed_nop,(volatile void *) ADDR_ISTAT) | |
| BENCH (b_c, timed_burst,(volatile void *) ADDR_RAM_C) | |
| BENCH (b_u, timed_burst,(volatile void *) ADDR_RAM_U) | |
| ramsyscall_printf ("=== store cost by mirror (N=%d sequential words) ===\n", N_READS) | |
| report ("store KSEG0:", w_c, base) | |
| report ("store KSEG1:", w_u, base) | |
| ramsyscall_printf (" --- saturating burst, 1 instr/store, nop baseline raw256=%u ---\n", nb) | |
| report ("burst KSEG0:", b_c, nb) | |
| report ("burst KSEG1:", b_u, nb) | |
| CESTER_MAYBE_TEST (kseg1StoreSync, load_tests, volatile void *C=(volatile void *) ADDR_RAM_C;volatile void *U=(volatile void *) ADDR_RAM_U;ramsyscall_printf("=== KSEG1 store sync vs primed queue depth (16 reps) ===\n");ramsyscall_printf(" Format: PRIME n=<KSEG0 stores ahead> sync=<ticks> base=<ticks> delta=<ticks per 16>\n");primeRow(0, prime0, pbase0, C, U);primeRow(1, prime1, pbase1, C, U);primeRow(2, prime2, pbase2, C, U);primeRow(3, prime3, pbase3, C, U);primeRow(4, prime4, pbase4, C, U);primeRow(5, prime5, pbase5, C, U);primeRow(6, prime6, pbase6, C, U);) CESTER_MAYBE_TEST(readAfterWrite | |
| for (int i=0;i< 8;i++) | |
Variables | |
| uint32_t | d0 = stride0 > ref ? stride0 - ref : ref - stride0 |
| load_tests | |
| volatile void * | c0 = (volatile void *)ADDR_RAM_C |
| volatile void * | c1 = (volatile void *)(ADDR_RAM_C + 4096u) |
| volatile void * | u0 = (volatile void *)ADDR_RAM_U |
| volatile void * | u1 = (volatile void *)(ADDR_RAM_U + 4096u) |
| uint32_t | sc = 0xffffu |
| uint32_t | oc = 0xffffu |
| uint32_t | su = 0xffffu |
| uint32_t | ou = 0xffffu |
| uint32_t | nl = 0xffffu |
| #define ADDR_BIOS 0xbfc00000u /* BIOS ROM (KSEG1) */ |
| #define ADDR_DMA 0xbf8010a0u /* on-die MMIO: DMA channel 2 MADR */ |
| #define ADDR_ISTAT 0xbf801070u /* on-die MMIO: interrupt controller I_STAT */ |
| #define ADDR_RAM_C 0x80100000u /* main RAM, cached mirror (KSEG0) */ |
| #define ADDR_SCRATCH 0x1f800000u /* scratchpad SRAM (fast on-chip, no bus) */ |
| #define BENCH | ( | ret, | |
| fn, | |||
| p | |||
| ) |
| #define BIU_CONFIG_ADDR 0xfffe0130u |
| #define BIU_EXPECTED 0x0001e988u |
| #define CESTER_MAYBE_TEST CESTER_TEST |
| #define CESTER_NO_SIGNAL |
| #define CESTER_NO_TIME |
| #define EXIT_FAILURE 1 |
| #define EXIT_SUCCESS 0 |
| #define MAKE_GAP | ( | name, | |
| op | |||
| ) |
| #define MAKE_PRIME | ( | name, | |
| primers, | |||
| tail | |||
| ) |
| #define N_LOADS 32 |
| #define N_READS 256 |
| #define PCSX_TESTS 0 |
| #define PRIME0 "" |
| #define PRIME1 PRIME0 "sw $0, 0(%0)\n" |
| #define PRIME2 PRIME1 "sw $0, 4(%0)\n" |
| #define PRIME3 PRIME2 "sw $0, 8(%0)\n" |
| #define PRIME4 PRIME3 "sw $0, 12(%0)\n" |
| #define PRIME5 PRIME4 "sw $0, 16(%0)\n" |
| #define PRIME6 PRIME5 "sw $0, 20(%0)\n" |
| #define REP4 | ( | x | ) | x x x x |
| #define SW16 |
| CESTER_BODY | ( | static int s_interruptsWereEnabled;static __attribute__((always_inline)) uint32_t timed_read(volatile void *p) { register uint32_t sink;uint16_t before, after;before=COUNTERS[2].value;__asm__ volatile(REP256("lw %0, 0(%1)\n") :"=&r"(sink) :"r"(p) :"memory");after=COUNTERS[2].value;(void) sink;return(uint16_t)(after - before);} static __attribute__((always_inline)) uint32_t timed_nop(volatile void *p) { register uint32_t sink;uint16_t before, after;before=COUNTERS[2].value;__asm__ volatile(REP256("nop\n") :"=&r"(sink) :"r"(p) :"memory");after=COUNTERS[2].value;(void) sink;return(uint16_t)(after - before);} static __attribute__((always_inline)) uint32_t timed_pair(volatile void *a, volatile void *b) { register uint32_t sink;uint16_t before, after;before=COUNTERS[2].value;__asm__ volatile(REP128("lw %0, 0(%1)\nlw %0, 0(%2)\n") :"=&r"(sink) :"r"(a), "r"(b) :"memory");after=COUNTERS[2].value;(void) sink;return(uint16_t)(after - before);} static __attribute__((always_inline)) uint32_t timed_write(volatile void *p) { uint16_t before, after;register volatile char *q=(volatile char *) p;before=COUNTERS[2].value;__asm__ volatile(REP256("sw $0, 0(%0)\naddiu %0, %0, 4\n") :"+r"(q) ::"memory");after=COUNTERS[2].value;return(uint16_t)(after - before);} static __attribute__((always_inline)) uint32_t timed_walk(volatile void *p) { uint16_t before, after;register volatile char *q=(volatile char *) p;before=COUNTERS[2].value;__asm__ volatile(REP256("nop\naddiu %0, %0, 4\n") :"+r"(q) ::"memory");after=COUNTERS[2].value;return(uint16_t)(after - before);} static __attribute__((always_inline)) uint32_t timed_burst(volatile void *p) { uint16_t before, after;before=COUNTERS[2].value;__asm__ volatile(REP16(SW16) ::"r"(p) :"memory");after=COUNTERS[2].value;return(uint16_t)(after - before);} typedef uint32_t(*)(volatile void *, volatile void *) PrimeFn;static void primeRow(int n, PrimeFn sync, PrimeFn base, volatile void *c, volatile void *u) { uint32_t bs=0xffffu, bb=0xffffu;for(int i=0;i< 8;i++) { uint32_t x=sync(c, u);uint32_t y=base(c, u);if(x< bs) bs=x;if(y< bb) bb=y;} ramsyscall_printf(" PRIME n=%d sync=%u base=%u delta=%d\n", n, bs, bb,(int) bs -(int) bb);} MAKE_RAW(raw_same, "sw $0, 0(%1)\nlw %0, 0(%1)\nnop\naddiu %0, %0, 1\n") MAKE_RAW(raw_other, "sw $0, 0(%1)\nlw %0, 0(%2)\nnop\naddiu %0, %0, 1\n") #define BENCH(ret, fn, p) static void report(const char *name, uint32_t raw, uint32_t base) { uint32_t abs_cc=(raw *100u+N_READS/2)/N_READS;uint32_t marg_cc=((raw - base) *100u+N_READS/2)/N_READS;ramsyscall_printf(" %s raw256=%u abs=%u.%02u marginal=%u.%02u cyc/read\n", name, raw, abs_cc/100u, abs_cc % 100u, marg_cc/100u, marg_cc % 100u);} | ) |
| CESTER_MAYBE_TEST | ( | kseg1StoreSync | , |
| load_tests | , | ||
| volatile void * | C = (volatile void *)ADDR_RAM_C; volatile void *U = (volatile void *)ADDR_RAM_U; ramsyscall_printf("=== KSEG1 store sync vs primed queue depth (16 reps) ===\n"); ramsyscall_printf(" Format: PRIME n=<KSEG0 stores ahead> sync=<ticks> base=<ticks> delta=<ticks per 16>\n"); primeRow(0, prime0, pbase0, C, U); primeRow(1, prime1, pbase1, C, U); primeRow(2, prime2, pbase2, C, U); primeRow(3, prime3, pbase3, C, U); primeRow(4, prime4, pbase4, C, U); primeRow(5, prime5, pbase5, C, U); primeRow(6, prime6, pbase6, C, U); |
||
| ) |
| CESTER_MAYBE_TEST | ( | loadCostByGap | , |
| load_tests | , | ||
| volatile void * | A = (volatile void *)ADDR_RAM_U; ramsyscall_printf("=== load cost by inter-access gap (32 reps, one uncached RAM address) ===\n"); ramsyscall_printf(" Format: GAP d=<delay loop count> load=<ticks> nop=<ticks> delta=<ticks per 32 loads>\n"); for (uint32_t d = 1; d <= 48; d++) { uint32_t bl = 0xffffu, bn = 0xffffu; for (int i = 0; i < 8; i++) { uint32_t x = gap_load(A, d); uint32_t y = gap_nop(A, d); if (x < bl) bl = x; if (y < bn) bn = y; } ramsyscall_printf(" GAP d=%u load=%u nop=%u delta=%u\n", d, bl, bn, bl > bn ? bl - bn : 0u); } |
||
| ) |
| for | ( | ) |
| ramsyscall_printf | ( | " --- saturating | burst, |
| 1 instr/ | store | ||
| ) |
| ramsyscall_printf | ( | ) |
| report | ( | "burst KSEG0:" | , |
| b_c | , | ||
| nb | |||
| ) |
| report | ( | "burst KSEG1:" | , |
| b_u | , | ||
| nb | |||
| ) |
| report | ( | "store KSEG0:" | , |
| w_c | , | ||
| base | |||
| ) |
| report | ( | "store KSEG1:" | , |
| w_u | , | ||
| base | |||
| ) |
| volatile void* c0 = (volatile void *)ADDR_RAM_C |
| volatile void* c1 = (volatile void *)(ADDR_RAM_C + 4096u) |
| uint32_t d0 = stride0 > ref ? stride0 - ref : ref - stride0 |
| load_tests |
| uint32_t nl = 0xffffu |
| uint32_t oc = 0xffffu |
| uint32_t ou = 0xffffu |
| uint32_t sc = 0xffffu |
| uint32_t su = 0xffffu |
| volatile void* u0 = (volatile void *)ADDR_RAM_U |
| volatile void* u1 = (volatile void *)(ADDR_RAM_U + 4096u) |