flowrs-engine/tools/qemu-mips-wasm/fixtures/mz/main.c
flowRS 49c9d04fbc flowRS engine, cut from flowRS bd343e051a2e
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]>
2026-11-05 13:37:09 -05:00

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();
}
}
}