Built alone from an empty CARGO_HOME, its tests passed, and flowrsc checked, ran and exported a document from the conformance corpus; the export built alone. Co-Authored-By: Claude Opus 5.5 <[email protected]>
452 lines
13 KiB
C
452 lines
13 KiB
C
/*
|
|
* 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 <stdint.h>
|
|
|
|
#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();
|
|
}
|
|
}
|
|
}
|