/* * The PIC32 twin's bare-metal fixture: no library, no framework, registers only, so that what * it states is about the machine and nothing between. Built with clang for mipsel (MIPS32r2) * by build.sh; runs on the PIC32MX3xx/4xx and 5xx/6xx/7xx parts (their ports, Timer2, UART1 and * interrupt controller registers sit at the same addresses; DS61143H Table 4-x, DS60001156J). * * UART1 at 115200: the lines below; * RG6: toggled every 20000 core-timer counts by polling Count -- 1 kHz from Count's * SYSCLK/2 = 40 MHz (PIC32 FRM 2.12.3); * RF0: toggled by the core timer's interrupt every 1 ms, priority 7 on the shadow register * set -- 500 Hz; * RD1: toggled by Timer2's interrupt, priority 3 on the normal set, PBCLK/8/40000 = 250 Hz * periods -- 125 Hz, until the second phase. * * After DONE, a second phase checks four rules no measurement above depends on: the core timer's * handler on the shadow set read the interrupted code's stack pointer with RDPGPR (the * previous set's, not its own); a request is taken only above Status.IPL, not at it (IPL 3 * holds Timer2's priority-3 request, IPL 2 lets it in); OSCCON.PBDIV /2, written after the * SYSKEY unlock, halves the clock Timer2 counts; a 16-bit timer started above its period * register counts on to 0xFFFF and wraps to 0 (FRM 14.3.3). * * SPDX-License-Identifier: Apache-2.0 */ #include #define SFR(a) (*(volatile uint32_t *)(a)) #define CLR 1 #define SET 2 #define INV 3 #define REG(a, op) SFR((a) + 4 * (op)) #define U1MODE 0xBF806000u #define U1STA 0xBF806010u #define U1TXREG 0xBF806020u #define U1BRG 0xBF806040u #define TRISD 0xBF8860C0u #define PORTD 0xBF8860D0u #define LATD 0xBF8860E0u #define TRISF 0xBF886140u #define LATF 0xBF886160u #define TRISG 0xBF886180u #define LATG 0xBF8861A0u #define CNCON 0xBF8861C0u #define CNPUE 0xBF8861E0u #define T2CON 0xBF800800u #define TMR2 0xBF800810u #define PR2 0xBF800820u #define INTCON 0xBF881000u #define INTSTAT 0xBF881010u #define IFS0 0xBF881030u #define IEC0 0xBF881060u #define IPC0 0xBF881090u #define IPC2 0xBF8810B0u #define DEVID 0xBF80F220u #define SYSKEY 0xBF80F230u #define OSCCON 0xBF80F000u static inline uint32_t mfc0_count(void) { uint32_t v; __asm__ volatile("mfc0 %0, $9" : "=r"(v)); return v; } static inline uint32_t mfc0_srsctl(void) { uint32_t v; __asm__ volatile("mfc0 %0, $12, 2" : "=r"(v)); return v; } static inline void mtc0_compare(uint32_t v) { __asm__ volatile("mtc0 %0, $11" : : "r"(v)); } static inline uint32_t mfc0_status(void) { uint32_t v; __asm__ volatile("mfc0 %0, $12" : "=r"(v)); return v; } static inline void mtc0_status(uint32_t v) { __asm__ volatile("mtc0 %0, $12\n\tehb" : : "r"(v)); } /* Busy-wait n counts of Count (40 MHz). */ static void wait_counts(uint32_t n) { uint32_t t = mfc0_count(); while (mfc0_count() - t < n) { } } static void putc_(char c) { while (SFR(U1STA) & (1u << 9)) { /* UTXBF */ } SFR(U1TXREG) = (uint8_t)c; } static void puts_(const char *s) { while (*s) { putc_(*s++); } } static void put_hex(uint32_t v) { puts_("0x"); for (int i = 28; i >= 0; i -= 4) { putc_("0123456789abcdef"[(v >> i) & 0xf]); } } static void put_dec(uint32_t v) { char b[11]; int n = 0; do { b[n++] = '0' + v % 10; v /= 10; } while (v); while (n) { putc_(b[--n]); } } static volatile uint32_t ct_ticks, ct_srs, t2_ticks, t2_srs, ct_next; /* The stack pointer the core timer's handler took from the previous set (start.S). */ volatile uint32_t ct_seen_sp; void ct_handler(void) { if (!ct_ticks) { ct_srs = mfc0_srsctl(); } ct_ticks++; ct_next += 40000; /* 1 ms at 40 MHz */ mtc0_compare(ct_next); REG(IFS0, CLR) = 1u << 0; /* CTIF */ REG(LATF, INV) = 1u << 0; /* RF0 */ } void t2_handler(void) { if (!t2_ticks) { t2_srs = mfc0_srsctl(); } t2_ticks++; REG(IFS0, CLR) = 1u << 8; /* T2IF */ REG(LATD, INV) = 1u << 1; /* RD1 */ } void exc_handler(void) { uint32_t cause, epc; __asm__ volatile("mfc0 %0, $13" : "=r"(cause)); __asm__ volatile("mfc0 %0, $14" : "=r"(epc)); puts_("EXC cause="); put_hex(cause); puts_(" epc="); put_hex(epc); puts_("\n"); for (;;) { } } /* Timer2 at 1:1 from `start` for 40 counts of Count; what TMR2 reads then. */ static uint32_t t2_count_from(uint32_t start) { uint32_t tmr; SFR(T2CON) = 0; SFR(PR2) = 100; SFR(TMR2) = start; SFR(T2CON) = 1u << 15; wait_counts(40); tmr = SFR(TMR2); SFR(T2CON) = 0; return tmr; } /* The second phase (see the top of the file). */ static void phase2(void) { uint32_t sp, st, before, held, ran, tmr; __asm__ volatile("move %0, $sp" : "=r"(sp)); puts_("SRS rdpgpr sp ok="); put_dec(ct_seen_sp <= sp && sp - ct_seen_sp < 1024); puts_("\n"); /* Status.IPL is bits 15:10 with an external interrupt controller (MIPS32 PRA, EIC mode). */ st = mfc0_status(); before = t2_ticks; mtc0_status((st & ~(0x3fu << 10)) | (3u << 10)); wait_counts(400000); /* 10 ms: two and a half Timer2 periods */ held = t2_ticks == before; before = t2_ticks; mtc0_status((st & ~(0x3fu << 10)) | (2u << 10)); wait_counts(400000); ran = t2_ticks >= before + 2; mtc0_status(st); puts_("IPL3 t2 held="); put_dec(held); puts_(" IPL2 t2 ran="); put_dec(ran); puts_("\n"); /* PBCLK /2: Timer2 at 1:1 counts 2000 ticks in 2000 counts of Count (40 MHz), not 4000. */ REG(IEC0, CLR) = 1u << 8; SFR(T2CON) = 0; SFR(TMR2) = 0; SFR(PR2) = 0xffff; SFR(SYSKEY) = 0; SFR(SYSKEY) = 0xAA996655u; SFR(SYSKEY) = 0x556699AAu; REG(OSCCON, CLR) = 3u << 19; REG(OSCCON, SET) = 1u << 19; /* PBDIV 01: /2 */ SFR(SYSKEY) = 0; SFR(T2CON) = 1u << 15; /* ON, 1:1 */ wait_counts(2000); tmr = SFR(TMR2); SFR(T2CON) = 0; puts_("PBDIV2 tmr2/100="); put_dec((tmr + 50) / 100); puts_("\n"); /* Started at 0xFFF0, above PR2 = 100, it takes sixteen ticks to the wrap and goes on from 0: * it reads sixteen less than the same wait started from 0 (give or take the tick the two * starts may fall either side of). */ before = t2_count_from(0); tmr = t2_count_from(0xfff0); puts_("T2 above PR wraps ok="); put_dec(before - tmr >= 15 && before - tmr <= 17); puts_("\nPHASE2 DONE\n"); } int main(void) { uint32_t next, nopull, pullup; /* 115200 at PBCLK 80 MHz: BRG = 80e6 / (16 * 115200) - 1 = 42 (FRM 21.3). */ SFR(U1BRG) = 42; SFR(U1STA) = (1u << 10) | (1u << 12); /* UTXEN, URXEN */ SFR(U1MODE) = 1u << 15; /* ON, 8N1 */ puts_("PIC32 bare-metal: start\n"); puts_("DEVID="); put_hex(SFR(DEVID)); puts_(" OSCCON="); put_hex(SFR(OSCCON)); puts_("\n"); /* An input: RD5 (CN14) with nothing on it, then with its weak pull-up (FRM 12.2.6). */ nopull = (SFR(PORTD) >> 5) & 1; REG(CNPUE, SET) = 1u << 14; pullup = (SFR(PORTD) >> 5) & 1; REG(CNPUE, CLR) = 1u << 14; puts_("IN RD5 nopull="); put_dec(nopull); puts_(" pullup="); put_dec(pullup); puts_("\n"); REG(TRISG, CLR) = 1u << 6; REG(TRISF, CLR) = 1u << 0; REG(TRISD, CLR) = 1u << 1; /* Multi-vector mode; the core timer at priority 7 (IPC0<4:2>), Timer2 at 3 (IPC2<4:2>). */ REG(INTCON, SET) = 1u << 12; SFR(IPC0) = 7u << 2; SFR(IPC2) = 3u << 2; ct_next = mfc0_count() + 40000; mtc0_compare(ct_next); SFR(T2CON) = 0; SFR(TMR2) = 0; SFR(PR2) = 39999; SFR(T2CON) = (1u << 15) | (3u << 4); /* ON, 1:8 */ REG(IFS0, CLR) = (1u << 0) | (1u << 8); REG(IEC0, SET) = (1u << 0) | (1u << 8); __asm__ volatile("ei; ehb"); next = mfc0_count(); for (;;) { next += 20000; while ((int32_t)(mfc0_count() - next) < 0) { } REG(LATG, INV) = 1u << 6; /* RG6 */ if (ct_ticks == 200) { puts_("CT ticks=200 css="); put_dec(ct_srs & 0xf); puts_(" pss="); put_dec((ct_srs >> 6) & 0xf); puts_(" T2 ticks="); put_dec(t2_ticks); puts_(" css="); put_dec(t2_srs & 0xf); puts_(" main css="); put_dec(mfc0_srsctl() & 0xf); puts_("\nDONE\n"); ct_ticks++; /* say it once */ phase2(); } } }