/* * The PIC32 twin's bare-metal fixture for the PIC32MZ: the EC (microAptiv UP, no FPU) and the * EF (M-Class, with its FPU), whose registers sit at the same addresses but for their ADCs. * Registers only, built with clang (build.sh), for the PIC32MZ2048ECG100 and EFG100: every * address, IRQ, vector and PPS code is the parts' EDC files' (DS60001191G, DS60001320H). * * UART1 at 115200 from PBCLK2 (100 MHz), U1TX on RPD3 (RPD3R = 0001): the lines below; * RE0: toggled every 50000 core-timer counts by polling Count -- 1 kHz from Count's * SYSCLK/2 = 100 MHz; * RE1: toggled by the core timer's interrupt every 1 ms, priority 7 on shadow set 1 * (PRISS.PRI7SS) -- 500 Hz; * RE2: toggled by Timer2's interrupt, priority 3 on the normal set, PBCLK3/8/50000 = 250 Hz * periods -- 125 Hz; * RD1: OC1 through PPS (RPD1R = 1100), PWM from Timer3 at 1 kHz (PBCLK3 / 8 / 12500), 25 %. * The vectors are where OFF000 and OFF009 say (variable offsets, DS60001108H 8.5.2). * * Then, once: the core's identity and its caches (PRId, Config, Config1's geometry, Config3, * Config.K0 made write-back); on the EF the FPU in FR=1 mode (sqrt.d of 2.0) and ADC0 through * its individual conversion request (AN0 held at 1650 mV: 12 bits against 3.3 V); change notice * on port E -- RE3 drives RE4 through a jumper the runner wires, nine changes -- in the EF's * edge mode on rising edges only (CNCONE.EDGEDETECT, CNENE: five) and in mismatch mode on the * EC, which has no edge mode (nine); PB3DIV /4 after the SYSKEY unlock halves what Timer2 * counts; REFO1 turned on reads ACTIVE; with CFGCON.IOLOCK set a write to RPD1R changes * nothing, and with DEVCFG3.IOL1WAY 1 IOLOCK does not clear. * * 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 CFGCON 0xBF800000u #define DEVID 0xBF800020u #define SYSKEY 0xBF800030u #define OSCCON 0xBF801200u #define SPLLCON 0xBF801220u #define REFO1CON 0xBF801280u #define PB3DIV 0xBF801320u #define U1RXR 0xBF801468u #define RPD1R 0xBF8015C4u #define RPD3R 0xBF8015CCu #define INTCON 0xBF810000u #define PRISS 0xBF810010u #define IFS0 0xBF810040u #define IFS3 0xBF810070u #define IEC0 0xBF8100C0u #define IEC3 0xBF8100F0u #define IPC0 0xBF810140u #define IPC2 0xBF810160u #define IPC30 0xBF810320u #define OFF000 0xBF810540u #define OFF009 0xBF810564u #define OFF122 0xBF810728u #define U1MODE 0xBF822000u #define U1STA 0xBF822010u #define U1TXREG 0xBF822020u #define U1BRG 0xBF822040u #define T2CON 0xBF840200u #define TMR2 0xBF840210u #define PR2 0xBF840220u #define T3CON 0xBF840400u #define TMR3 0xBF840410u #define PR3 0xBF840420u #define OC1CON 0xBF844000u #define OC1R 0xBF844010u #define OC1RS 0xBF844020u #define ADCCON1 0xBF84B000u #define ADCCON2 0xBF84B004u #define ADCCON3 0xBF84B008u #define ADC0TIME 0xBF84B0D4u #define ADCDSTAT1 0xBF84B030u #define ADCANCON 0xBF84B100u #define ADCDATA0 0xBF84B200u #define ANSELB 0xBF860100u #define ANSELD 0xBF860300u #define TRISD 0xBF860310u #define ANSELE 0xBF860400u #define TRISE 0xBF860410u #define PORTE 0xBF860420u #define LATE 0xBF860430u #define CNCONE 0xBF860470u #define CNENE 0xBF860480u #define CNSTATE 0xBF860490u #define CNNEE 0xBF8604A0u #define CNFE 0xBF8604B0u #define MFC0(reg, sel, v) __asm__ volatile("mfc0 %0, $" #reg ", " #sel : "=r"(v)) #define MTC0(reg, sel, v) __asm__ volatile("mtc0 %0, $" #reg ", " #sel "\n\tehb" : : "r"(v)) extern void ct_isr(void), t2_isr(void), cn_isr(void); extern uint64_t fpu_sqrt2(void); static inline uint32_t mfc0_count(void) { uint32_t v; __asm__ volatile("mfc0 %0, $9" : "=r"(v)); return v; } static inline void mtc0_compare(uint32_t v) { __asm__ volatile("mtc0 %0, $11" : : "r"(v)); } static inline uint32_t mfc0_srsctl(void) { uint32_t v; __asm__ volatile("mfc0 %0, $12, 2" : "=r"(v)); return v; } /* Busy-wait n counts of Count (100 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 void unlock(void) { SFR(SYSKEY) = 0; SFR(SYSKEY) = 0xAA996655u; SFR(SYSKEY) = 0x556699AAu; } static volatile uint32_t ct_ticks, ct_srs, t2_ticks, t2_srs, ct_next, cn_hits; /* 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 += 100000; /* 1 ms at 100 MHz */ mtc0_compare(ct_next); REG(IFS0, CLR) = 1u << 0; /* CTIF */ REG(LATE, INV) = 1u << 1; /* RE1 */ } void t2_handler(void) { if (!t2_ticks) { t2_srs = mfc0_srsctl(); } t2_ticks++; REG(IFS0, CLR) = 1u << 9; /* T2IF */ REG(LATE, INV) = 1u << 2; /* RE2 */ } /* In edge mode the flags are CNFE's, cleared by software; in mismatch mode a read of PORTE * ends the mismatch (FRM 12.3.3.1). */ void cn_handler(void) { if (SFR(CNCONE) & (1u << 11)) { REG(CNFE, CLR) = SFR(CNFE); } else { (void)SFR(PORTE); } cn_hits++; REG(IFS3, CLR) = 1u << 26; /* CNEIF: IRQ 122 */ } 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 for 2000 counts of Count; what TMR2 reads then. */ static uint32_t t2_count(void) { uint32_t tmr; SFR(T2CON) = 0; SFR(TMR2) = 0; SFR(PR2) = 0xffff; SFR(T2CON) = 1u << 15; wait_counts(2000); tmr = SFR(TMR2); SFR(T2CON) = 0; return tmr; } static void once(void) { uint32_t prid, cfg, cfg1, cfg3, k0, sp, before, after, lock, cleared, refo, t100, t50; int ef; MFC0(15, 0, prid); MFC0(16, 0, cfg); MFC0(16, 1, cfg1); MFC0(16, 3, cfg3); ef = cfg1 & 1; /* Config1.FP: the EF */ puts_("PRId="); put_hex(prid); puts_(" Config="); put_hex(cfg); puts_(" Config1="); put_hex(cfg1); puts_(" Config3="); put_hex(cfg3); puts_("\n"); /* Config1 (Register 50-31): sets 64 << S, line 2 << L, ways A + 1. */ puts_("I-cache "); put_dec(((64u << ((cfg1 >> 22) & 7)) * (2u << ((cfg1 >> 19) & 7)) * (((cfg1 >> 16) & 7) + 1)) / 1024); puts_(" KB D-cache "); put_dec(((64u << ((cfg1 >> 13) & 7)) * (2u << ((cfg1 >> 10) & 7)) * (((cfg1 >> 7) & 7) + 1)) / 1024); MTC0(16, 0, (cfg & ~7u) | 3); /* kseg0 write-back, write allocate */ MFC0(16, 0, k0); puts_(" KB K0="); put_dec(k0 & 7); puts_("\n"); __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"); if (ef) { uint32_t st, fir; uint64_t r; MFC0(12, 0, st); MTC0(12, 0, st | 0x24000000u); /* Status.CU1 and FR */ __asm__ volatile(".set push\n\t.set hardfloat\n\tcfc1 %0, $0\n\t.set pop" : "=r"(fir)); r = fpu_sqrt2(); puts_("FIR="); put_hex(fir); puts_(" sqrt2="); put_hex((uint32_t)(r >> 32)); put_hex((uint32_t)r); puts_("\n"); /* ADC0 (AN0, RB0): on, warmed up, digital enable, one request (FRM 22.4.9). */ REG(ANSELB, SET) = 1u << 0; SFR(ADCCON1) = 0; SFR(ADCCON2) = 0; SFR(ADCCON3) = 1u << 30; /* ADCSEL 01: SYSCLK; CONCLKDIV 0 */ SFR(ADC0TIME) = (3u << 24) | (4u << 16) | 5; /* 12 bits, TAD 8 TQ, SAMC 5 */ SFR(ADCANCON) = 5u << 24; /* WKUPCLKCNT: 32 clocks */ SFR(ADCCON1) = (3u << 21) | (1u << 15); /* SELRES 12 bits, ON */ while (!(SFR(ADCCON2) & (1u << 31))) { /* BGVRRDY */ } SFR(ADCANCON) |= 1u << 0; /* ANEN0 */ while (!(SFR(ADCANCON) & (1u << 8))) { /* WKRDY0 */ } SFR(ADCCON3) |= 1u << 16; /* DIGEN0 */ SFR(ADCCON3) = (SFR(ADCCON3) & ~0x3fu) | (1u << 8); /* RQCNVRT, ADINSEL 0 */ while (!(SFR(ADCDSTAT1) & 1u)) { } puts_("ADC an0="); put_dec(SFR(ADCDATA0)); puts_(" ready after read="); put_dec(SFR(ADCDSTAT1) & 1u); puts_("\n"); } else { puts_("no FPU\n"); } /* Change notice on port E: RE4 enabled, RE3 drives it through the jumper. */ REG(ANSELE, CLR) = (1u << 3) | (1u << 4); REG(TRISE, CLR) = 1u << 3; REG(LATE, CLR) = 1u << 3; SFR(CNCONE) = (1u << 15) | (1u << 11); /* ON, EDGEDETECT where there is one */ SFR(CNENE) = 1u << 4; /* rising (edge mode), any change (mismatch) */ SFR(CNNEE) = 0; (void)SFR(PORTE); SFR(IPC30) = 2u << 18; /* vector 122: IPC30<20:18> */ SFR(OFF122) = (uint32_t)cn_isr - 0x9D000000u; REG(IFS3, CLR) = 1u << 26; REG(IEC3, SET) = 1u << 26; for (int i = 0; i < 9; i++) { REG(LATE, INV) = 1u << 3; wait_counts(100000); } REG(IEC3, CLR) = 1u << 26; puts_("CN port E edge="); put_dec((SFR(CNCONE) >> 11) & 1); puts_(" hits="); put_dec(cn_hits); puts_("\n"); /* PB3DIV /4: Timer2 at 1:1 counts half as far in the same 2000 counts of Count. */ REG(IEC0, CLR) = 1u << 9; t100 = t2_count(); unlock(); SFR(PB3DIV) = (SFR(PB3DIV) & ~0x7fu) | 3; SFR(SYSKEY) = 0; t50 = t2_count(); puts_("PB3DIV2 tmr2="); put_dec((t100 + 50) / 100 * 100); puts_(" PB3DIV4 tmr2="); put_dec((t50 + 50) / 100 * 100); puts_("\n"); /* REFO1 from SYSCLK, divided by 4: on, ACTIVE follows. */ SFR(REFO1CON) = (2u << 16) | (1u << 15); refo = SFR(REFO1CON); puts_("REFO1 on="); put_dec((refo >> 15) & 1); puts_(" active="); put_dec((refo >> 8) & 1); puts_(" SPLLCON="); put_hex(SFR(SPLLCON)); puts_("\n"); /* IOLOCK: set after the unlock; a write to RPD1R is then ignored. */ before = SFR(RPD1R); unlock(); SFR(CFGCON) = SFR(CFGCON) | (1u << 13); SFR(SYSKEY) = 0; SFR(RPD1R) = 0; after = SFR(RPD1R); lock = (SFR(CFGCON) >> 13) & 1; unlock(); SFR(CFGCON) = SFR(CFGCON) & ~(1u << 13); SFR(SYSKEY) = 0; cleared = !((SFR(CFGCON) >> 13) & 1); puts_("IOLOCK="); put_dec(lock); puts_(" RPD1R kept="); put_dec(before == 12 && after == 12); puts_(" cleared="); put_dec(cleared); puts_("\nONCE DONE\n"); } int main(void) { uint32_t next; /* UART1 on RPD3; 115200 at PBCLK2 100 MHz with BRGH: 100e6 / (4 * 115200) - 1 = 216. */ SFR(RPD3R) = 1; /* U1TX (group 2, 0001) */ SFR(U1BRG) = 216; SFR(U1STA) = (1u << 10) | (1u << 12); /* UTXEN, URXEN */ SFR(U1MODE) = (1u << 15) | (1u << 3); /* ON, BRGH, 8N1 */ puts_("PIC32MZ bare-metal: start\n"); puts_("DEVID="); put_hex(SFR(DEVID)); puts_(" OSCCON="); put_hex(SFR(OSCCON)); puts_("\n"); SFR(ANSELE) = 0; REG(TRISE, CLR) = 7u; /* RE0-RE2 */ /* OC1 on RPD1: PWM (OCM 110) on Timer3 (OCTSEL), 1 kHz, OC1RS 3125 of 12500. */ REG(ANSELD, CLR) = 1u << 1; SFR(RPD1R) = 12; /* OC1 (group 4, 1100) */ SFR(T3CON) = 0; SFR(TMR3) = 0; SFR(PR3) = 12499; SFR(OC1RS) = 3125; SFR(OC1R) = 3125; SFR(OC1CON) = (1u << 15) | (1u << 3) | 6; SFR(T3CON) = (1u << 15) | (3u << 4); /* ON, 1:8 */ /* Multi-vector; core timer (vector 0) at 7 on shadow set 1, Timer2 (vector 9) at 3. */ SFR(OFF000) = (uint32_t)ct_isr - 0x9D000000u; SFR(OFF009) = (uint32_t)t2_isr - 0x9D000000u; SFR(PRISS) = 1u << 28; /* PRI7SS = 1 */ REG(INTCON, SET) = 1u << 12; SFR(IPC0) = 7u << 2; SFR(IPC2) = 3u << 10; /* vector 9: IPC2<12:10> */ ct_next = mfc0_count() + 100000; mtc0_compare(ct_next); SFR(T2CON) = 0; SFR(TMR2) = 0; SFR(PR2) = 49999; SFR(T2CON) = (1u << 15) | (3u << 4); /* ON, 1:8 */ REG(IFS0, CLR) = (1u << 0) | (1u << 9); REG(IEC0, SET) = (1u << 0) | (1u << 9); __asm__ volatile("ei; ehb"); next = mfc0_count(); for (;;) { next += 50000; while ((int32_t)(mfc0_count() - next) < 0) { } REG(LATE, INV) = 1u << 0; /* RE0 */ 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 */ once(); } } }