diff --git a/tests/pmu/Makefile b/tests/pmu/Makefile new file mode 100644 index 0000000..2fd6c28 --- /dev/null +++ b/tests/pmu/Makefile @@ -0,0 +1,3 @@ +TEST=pmu + +include ../Makefile.test diff --git a/tests/pmu/head.S b/tests/pmu/head.S new file mode 100644 index 0000000..03cffe4 --- /dev/null +++ b/tests/pmu/head.S @@ -0,0 +1,46 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define STACK_TOP 0x4000 + +/* Load an immediate 64-bit value into a register */ +#define LOAD_IMM64(r, e) \ + lis r,(e)@highest; \ + ori r,r,(e)@higher; \ + rldicr r,r, 32, 31; \ + oris r,r, (e)@h; \ + ori r,r, (e)@l; + + .section ".head","ax" + + /* + * Microwatt currently enters in LE mode at 0x0, so we don't need to + * do any endian fix ups> + */ + . = 0 +.global _start +_start: + b boot_entry + +.global boot_entry +boot_entry: + /* setup stack */ + LOAD_IMM64(%r1, STACK_TOP - 0x100) + LOAD_IMM64(%r12, main) + mtctr %r12, + bctrl + attn // terminate on exit + b . diff --git a/tests/pmu/pmu.c b/tests/pmu/pmu.c new file mode 100644 index 0000000..01bb53b --- /dev/null +++ b/tests/pmu/pmu.c @@ -0,0 +1,173 @@ +#include +#include + +#include "console.h" + + +#define asm __asm__ volatile + +#define MMCR0 795 +#define MMCR1 798 +#define MMCR2 785 +#define MMCRA 786 +#define PMC1 771 +#define PMC2 772 +#define PMC3 773 +#define PMC4 774 +#define PMC5 775 +#define PMC6 776 + +#define MMCR0_FC 0x80000000 // Freeze Counters +#define PMC1SEL_FC 0xFC000000 // Load Completed +#define PMC2SEL_F0 0x00F00000 // Store Completed + +#define TEST "Test " +#define PASS "PASS\n" +#define FAIL "FAIL\n" + +static inline unsigned long mfspr(int sprnum) +{ + unsigned long val; + + asm("mfspr %0,%1" : "=r" ((unsigned long) val) : "i" (sprnum)); + return val; +} + +static inline void mtspr(int sprnum, unsigned long val) +{ + asm("mtspr %0,%1" : : "i" (sprnum), "r" ((unsigned long) val)); +} + +void print_test_number(int i) +{ + puts(TEST); + putchar(48 + i/10); + putchar(48 + i%10); + putchar(':'); +} + +void reset_pmu() { + mtspr(MMCR0, MMCR0_FC); + mtspr(MMCR1, 0); + mtspr(PMC1, 0); + mtspr(PMC2, 0); + mtspr(PMC3, 0); + mtspr(PMC4, 0); + mtspr(PMC5, 0); + mtspr(PMC6, 0); +} + +/* + Sets PMC1 to count finished load instructions + Runs 50 load instructions + Expects PMC1 to be 50 at the end +*/ +int test_load_complete() +{ + reset_pmu(); + unsigned long volatile b = 0; + mtspr(MMCR1, PMC1SEL_FC); + mtspr(MMCR0, 0); + + for(int i = 0; i < 50; i++) + ++b; + + mtspr(MMCR0, MMCR0_FC); + + return mfspr(PMC1) == 50; +} + +/* + Sets PMC2 to count finished store instructions + Runs 50 store instructions + Expects PMC2 to be 50 at the end +*/ +int test_store_complete() +{ + reset_pmu(); + unsigned long volatile b = 0; + mtspr(MMCR1, PMC2SEL_F0); + mtspr(MMCR0, 0); + + for(int i = 0; i < 50; i++) + ++b; + + mtspr(MMCR0, MMCR0_FC); + + return mfspr(PMC2) == 50; +} + +/* + Allow PMC5 to count finished instructions + Runs a loop 50 times + Expects PMC5 to be more than zero at the end +*/ +int test_instruction_complete() +{ + reset_pmu(); + unsigned long volatile b = 0; + mtspr(MMCR0, 0); + + for(int i = 0; i < 50; i++) + ++b; + + mtspr(MMCR0, MMCR0_FC); + + return mfspr(PMC5) > 0; +} + +/* + Allow PMC6 to count cycles + Runs a loop 50 times + Expects PMC6 to be more than zero at the end +*/ +int test_count_cycles() +{ + reset_pmu(); + unsigned long volatile b = 0; + mtspr(MMCR0, 0); + + for(int i = 0; i < 50; i++) + ++b; + + mtspr(MMCR0, MMCR0_FC); + + return mfspr(PMC6) > 0; +} + +int main(void) +{ + int fail = 0; + + console_init(); + + print_test_number(1); + if (test_load_complete() != 1) { + fail = 1; + puts(FAIL); + } else + puts(PASS); + + print_test_number(2); + if (test_store_complete() != 1) { + fail = 1; + puts(FAIL); + } else + puts(PASS); + + print_test_number(3); + if (test_instruction_complete() == 0) { + fail = 1; + puts(FAIL); + } else + puts(PASS); + + print_test_number(4); + if (test_count_cycles() == 0) { + fail = 1; + puts(FAIL); + } else + puts(PASS); + + return fail; +} diff --git a/tests/pmu/powerpc.lds b/tests/pmu/powerpc.lds new file mode 100644 index 0000000..99611ab --- /dev/null +++ b/tests/pmu/powerpc.lds @@ -0,0 +1,27 @@ +SECTIONS +{ + . = 0; + _start = .; + .head : { + KEEP(*(.head)) + } + . = ALIGN(0x1000); + .text : { *(.text) *(.text.*) *(.rodata) *(.rodata.*) } + . = ALIGN(0x1000); + .data : { *(.data) *(.data.*) *(.got) *(.toc) } + . = ALIGN(0x80); + __bss_start = .; + .bss : { + *(.dynsbss) + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + *(.common) + *(.bss.*) + } + . = ALIGN(0x80); + __bss_end = .; + . = . + 0x4000; + __stack_top = .; +} diff --git a/tests/test_pmu.bin b/tests/test_pmu.bin new file mode 100755 index 0000000..0791139 Binary files /dev/null and b/tests/test_pmu.bin differ diff --git a/tests/test_pmu.console_out b/tests/test_pmu.console_out new file mode 100644 index 0000000..2ff5a99 --- /dev/null +++ b/tests/test_pmu.console_out @@ -0,0 +1,4 @@ +Test 01:PASS +Test 02:PASS +Test 03:PASS +Test 04:PASS diff --git a/tests/update_console_tests b/tests/update_console_tests index 4e013aa..b168e8d 100755 --- a/tests/update_console_tests +++ b/tests/update_console_tests @@ -3,7 +3,7 @@ # Script to update console related tests from source # -for i in sc illegal decrementer xics privileged mmu misc modes reservation trace fpu spr_read ; do +for i in sc illegal decrementer xics privileged mmu misc modes pmu reservation trace fpu spr_read ; do cd $i make cd -