Nugget
Bare-metal libraries and examples for the original PlayStation
Loading...
Searching...
No Matches
Macros | Functions | Variables
load-timings.c File Reference
#include "common/hardware/counters.h"
#include "common/syscalls/syscalls.h"
#include "exotic/cester.h"
Include dependency graph for load-timings.c:

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 voidc0 = (volatile void *)ADDR_RAM_C
 
volatile voidc1 = (volatile void *)(ADDR_RAM_C + 4096u)
 
volatile voidu0 = (volatile void *)ADDR_RAM_U
 
volatile voidu1 = (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
 

Macro Definition Documentation

◆ ADDR_BIOS

#define ADDR_BIOS   0xbfc00000u /* BIOS ROM (KSEG1) */

◆ ADDR_DMA

#define ADDR_DMA   0xbf8010a0u /* on-die MMIO: DMA channel 2 MADR */

◆ ADDR_ISTAT

#define ADDR_ISTAT   0xbf801070u /* on-die MMIO: interrupt controller I_STAT */

◆ ADDR_RAM_C

#define ADDR_RAM_C   0x80100000u /* main RAM, cached mirror (KSEG0) */

◆ ADDR_RAM_U

#define ADDR_RAM_U   0xa0100000u /* main RAM, uncached mirror (KSEG1) */

◆ ADDR_SCRATCH

#define ADDR_SCRATCH   0x1f800000u /* scratchpad SRAM (fast on-chip, no bus) */

◆ BENCH

#define BENCH (   ret,
  fn,
 
)
Value:
uint32_t ret; do { \
uint32_t best = 0xffffu; \
for (int i = 0; i < 8; i++) { \
uint32_t d = fn(p); \
if (d < best) best = d; \
} \
ret = best; \
} while (0);
int i
Definition gte-regio.c:287
static int ret
Definition syscalls.h:73
void uint32_t(classId, spec)

◆ BIU_CONFIG_ADDR

#define BIU_CONFIG_ADDR   0xfffe0130u

◆ BIU_EXPECTED

#define BIU_EXPECTED   0x0001e988u

◆ CESTER_MAYBE_TEST

#define CESTER_MAYBE_TEST   CESTER_TEST

◆ CESTER_NO_SIGNAL

#define CESTER_NO_SIGNAL

◆ CESTER_NO_TIME

#define CESTER_NO_TIME

◆ EXIT_FAILURE

#define EXIT_FAILURE   1

◆ EXIT_SUCCESS

#define EXIT_SUCCESS   0

◆ MAKE_GAP

#define MAKE_GAP (   name,
  op 
)
Value:
static __attribute__((always_inline)) uint32_t name(volatile void *p, uint32_t d) { \
register uint32_t sink; \
uint16_t before, after; \
before = COUNTERS[2].value; \
__asm__ volatile(REP32(op "\n" \
"nop\n" \
"addiu %0, %0, 1\n" \
"move $t9, %2\n" \
"1: addiu $t9, $t9, -1\n" \
"bgtz $t9, 1b\n" \
"nop\n") \
: "=&r"(sink) : "r"(p), "r"(d) : "$t9", "memory"); \
after = COUNTERS[2].value; \
(void)sink; \
return (uint16_t)(after - before); \
}
__attribute__((weak))
Definition clz.c:56
#define COUNTERS
Definition counters.h:40
#define REP32(x)
Definition load-timings.c:93
void void(ptr, size)
uint16_t before
Definition timers.c:145
int after
Definition timers.c:150

◆ MAKE_PRIME

#define MAKE_PRIME (   name,
  primers,
  tail 
)
Value:
static __attribute__((always_inline)) uint32_t name(volatile void *c, volatile void *u) { \
uint16_t before, after; \
before = COUNTERS[2].value; \
__asm__ volatile(REP16(primers tail) : : "r"(c), "r"(u) : "memory"); \
after = COUNTERS[2].value; \
return (uint16_t)(after - before); \
}
#define REP16(x)
Definition load-timings.c:92
static int c
Definition syscalls.h:122

◆ MAKE_RAW

#define MAKE_RAW (   name,
  seq 
)
Value:
static __attribute__((always_inline)) uint32_t name(volatile void *p, volatile void *q) { \
register uint32_t sink; \
uint16_t before, after; \
before = COUNTERS[2].value; \
__asm__ volatile(REP32(seq) : "=&r"(sink) : "r"(p), "r"(q) : "memory"); \
after = COUNTERS[2].value; \
(void)sink; \
return (uint16_t)(after - before); \
}

◆ MAKE_SPACED

#define MAKE_SPACED (   name,
  seq 
)
Value:
static __attribute__((always_inline)) uint32_t name(volatile void *p) { \
register uint32_t sink; \
uint16_t before, after; \
before = COUNTERS[2].value; \
__asm__ volatile(REP32(seq) : "=&r"(sink) : "r"(p) : "memory"); \
after = COUNTERS[2].value; \
(void)sink; \
return (uint16_t)(after - before); \
}

◆ N_LOADS

#define N_LOADS   32

◆ N_READS

#define N_READS   256

◆ PCSX_TESTS

#define PCSX_TESTS   0

◆ PRIME0

#define PRIME0   ""

◆ PRIME1

#define PRIME1   PRIME0 "sw $0, 0(%0)\n"

◆ PRIME2

#define PRIME2   PRIME1 "sw $0, 4(%0)\n"

◆ PRIME3

#define PRIME3   PRIME2 "sw $0, 8(%0)\n"

◆ PRIME4

#define PRIME4   PRIME3 "sw $0, 12(%0)\n"

◆ PRIME5

#define PRIME5   PRIME4 "sw $0, 16(%0)\n"

◆ PRIME6

#define PRIME6   PRIME5 "sw $0, 20(%0)\n"

◆ REP128

#define REP128 (   x)    REP64(x) REP64(x)

◆ REP16

#define REP16 (   x)    REP4(x) REP4(x) REP4(x) REP4(x)

◆ REP256

#define REP256 (   x)    REP64(x) REP64(x) REP64(x) REP64(x)

◆ REP32

#define REP32 (   x)    REP16(x) REP16(x)

◆ REP4

#define REP4 (   x)    x x x x

◆ REP64

#define REP64 (   x)    REP16(x) REP16(x) REP16(x) REP16(x)

◆ SW16

#define SW16
Value:
"sw $0, 0(%0)\n" "sw $0, 4(%0)\n" "sw $0, 8(%0)\n" "sw $0, 12(%0)\n" \
"sw $0, 16(%0)\n" "sw $0, 20(%0)\n" "sw $0, 24(%0)\n" "sw $0, 28(%0)\n" \
"sw $0, 32(%0)\n" "sw $0, 36(%0)\n" "sw $0, 40(%0)\n" "sw $0, 44(%0)\n" \
"sw $0, 48(%0)\n" "sw $0, 52(%0)\n" "sw $0, 56(%0)\n" "sw $0, 60(%0)\n"

Function Documentation

◆ BENCH() [1/6]

BENCH ( b_c  ,
timed_burst  ,
(volatile void *)  ADDR_RAM_C 
)

◆ BENCH() [2/6]

BENCH ( b_u  ,
timed_burst  ,
(volatile void *)  ADDR_RAM_U 
)

◆ BENCH() [3/6]

BENCH ( base  ,
timed_walk  ,
(volatile void *)  ADDR_RAM_C 
)

◆ BENCH() [4/6]

BENCH ( nb  ,
timed_nop  ,
(volatile void *)  ADDR_ISTAT 
)

◆ BENCH() [5/6]

BENCH ( w_c  ,
timed_write  ,
(volatile void *)  ADDR_RAM_C 
)

◆ BENCH() [6/6]

BENCH ( w_u  ,
timed_write  ,
(volatile void *)  ADDR_RAM_U 
)

◆ cester_assert_true()

cester_assert_true ( d0<=(uint32_t) N_READS 8u)

◆ CESTER_BODY()

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() [1/2]

CESTER_MAYBE_TEST ( kseg1StoreSync  ,
load_tests  ,
volatile void C = (volatile void *)ADDR_RAM_Cvolatile void *U = (volatile void *)ADDR_RAM_Uramsyscall_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() [2/2]

CESTER_MAYBE_TEST ( loadCostByGap  ,
load_tests  ,
volatile void A = (volatile void *)ADDR_RAM_Uramsyscall_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()

for ( )

◆ ramsyscall_printf() [1/2]

ramsyscall_printf ( " --- saturating  burst,
1 instr/  store 
)

◆ ramsyscall_printf() [2/2]

ramsyscall_printf ( )

◆ report() [1/4]

report ( "burst KSEG0:"  ,
b_c  ,
nb   
)

◆ report() [2/4]

report ( "burst KSEG1:"  ,
b_u  ,
nb   
)

◆ report() [3/4]

report ( "store KSEG0:"  ,
w_c  ,
base   
)

◆ report() [4/4]

report ( "store KSEG1:"  ,
w_u  ,
base   
)

Variable Documentation

◆ c0

◆ c1

volatile void* c1 = (volatile void *)(ADDR_RAM_C + 4096u)

◆ d0

uint32_t d0 = stride0 > ref ? stride0 - ref : ref - stride0

◆ load_tests

load_tests

◆ nl

uint32_t nl = 0xffffu

◆ oc

uint32_t oc = 0xffffu

◆ ou

uint32_t ou = 0xffffu

◆ sc

uint32_t sc = 0xffffu

◆ su

uint32_t su = 0xffffu

◆ u0

◆ u1

volatile void* u1 = (volatile void *)(ADDR_RAM_U + 4096u)