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]>
349 lines
10 KiB
C
349 lines
10 KiB
C
/*
|
|
* The PIC32 twin's bare-metal fixture for the PIC32MX1xx/2xx, whose peripherals reach their
|
|
* pins through peripheral pin select (PPS; PIC32 FRM Section 12.3.1, DS60001120F). Registers
|
|
* only, built with clang (build.sh), for the PIC32MX250F128B: every address, IRQ, vector and PPS
|
|
* code below is the part's, from its EDC file (DS60001168L Tables 7-1, 11-1, 11-2).
|
|
*
|
|
* UART1 at 115200, U1TX on RPB3 (RPB3R = 0001): the lines below;
|
|
* RB13: toggled every 10000 core-timer counts by polling Count -- 1 kHz from Count's
|
|
* SYSCLK/2 = 20 MHz;
|
|
* RB14: toggled by the core timer's interrupt every 1 ms, priority 7 -- 500 Hz;
|
|
* RB8: toggled by Timer2's interrupt, priority 3, PBCLK/8/20000 = 250 Hz periods -- 125 Hz;
|
|
* OC1: PWM from Timer3 at 1 kHz, 25 %, on RPB7 (RPB7R = 0101) for the first 100 ms, then
|
|
* moved to RPB15: RB7 stops, RB15 goes on.
|
|
*
|
|
* Then, once: INT4 read through PPS counts the rising edges RB5 makes through a jumper the
|
|
* runner wires to RA0: none with INT4R = 0010 (RPB4), five of nine with 0000 (RPA0); change
|
|
* notice by port --
|
|
* CNCONB.ON, CNENB.11 -- counts the four changes RB10 makes on RB11 through a jumper, each
|
|
* cleared by reading PORTB; an analog pin (ANSELB.2 set at reset) held high reads 0 until its
|
|
* ANSEL bit is cleared; RB4 reads its pull-up (CNPUB); with CFGCON.IOLOCK set (after the SYSKEY
|
|
* unlock) a write to RPB15R 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 U1MODE 0xBF806000u
|
|
#define U1STA 0xBF806010u
|
|
#define U1TXREG 0xBF806020u
|
|
#define U1BRG 0xBF806040u
|
|
#define T2CON 0xBF800800u
|
|
#define TMR2 0xBF800810u
|
|
#define PR2 0xBF800820u
|
|
#define T3CON 0xBF800A00u
|
|
#define TMR3 0xBF800A10u
|
|
#define PR3 0xBF800A20u
|
|
#define OC1CON 0xBF803000u
|
|
#define OC1R 0xBF803010u
|
|
#define OC1RS 0xBF803020u
|
|
#define INTCON 0xBF881000u
|
|
#define IFS0 0xBF881030u
|
|
#define IFS1 0xBF881040u
|
|
#define IEC0 0xBF881060u
|
|
#define IEC1 0xBF881070u
|
|
#define IPC0 0xBF881090u
|
|
#define IPC2 0xBF8810B0u
|
|
#define IPC4 0xBF8810D0u
|
|
#define IPC8 0xBF881110u
|
|
#define ANSELA 0xBF886000u
|
|
#define TRISA 0xBF886010u
|
|
#define ANSELB 0xBF886100u
|
|
#define TRISB 0xBF886110u
|
|
#define PORTB 0xBF886120u
|
|
#define LATB 0xBF886130u
|
|
#define CNPUB 0xBF886150u
|
|
#define CNCONB 0xBF886170u
|
|
#define CNENB 0xBF886180u
|
|
#define CNSTATB 0xBF886190u
|
|
#define CFGCON 0xBF80F200u
|
|
#define DEVID 0xBF80F220u
|
|
#define SYSKEY 0xBF80F230u
|
|
#define OSCCON 0xBF80F000u
|
|
#define INT4R 0xBF80FA10u
|
|
#define RPB3R 0xBF80FB38u
|
|
#define RPB7R 0xBF80FB48u
|
|
#define RPB15R 0xBF80FB68u
|
|
|
|
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));
|
|
}
|
|
|
|
/* Busy-wait n counts of Count (20 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, t2_ticks, ct_next, int4_hits, cn_hits;
|
|
|
|
void ct_handler(void)
|
|
{
|
|
ct_ticks++;
|
|
ct_next += 20000; /* 1 ms at 20 MHz */
|
|
mtc0_compare(ct_next);
|
|
REG(IFS0, CLR) = 1u << 0; /* CTIF */
|
|
REG(LATB, INV) = 1u << 14; /* RB14 */
|
|
}
|
|
|
|
void t2_handler(void)
|
|
{
|
|
t2_ticks++;
|
|
REG(IFS0, CLR) = 1u << 9; /* T2IF: IRQ 9 */
|
|
REG(LATB, INV) = 1u << 8; /* RB8 */
|
|
}
|
|
|
|
void int4_handler(void)
|
|
{
|
|
int4_hits++;
|
|
REG(IFS0, CLR) = 1u << 23; /* INT4IF: IRQ 23 */
|
|
}
|
|
|
|
/* Reading PORTB ends the mismatch; then the flag is cleared (FRM 12.3.3.1). */
|
|
void cn_handler(void)
|
|
{
|
|
(void)SFR(PORTB);
|
|
cn_hits++;
|
|
REG(IFS1, CLR) = 1u << 14; /* CNBIF: IRQ 46 */
|
|
}
|
|
|
|
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 (;;) {
|
|
}
|
|
}
|
|
|
|
static void once(void)
|
|
{
|
|
uint32_t held, digital, nopull, pullup, before, after, lock, unlocked, elsewhere;
|
|
|
|
/*
|
|
* INT4 through PPS: RB5 drives RA0 through the jumper. With INT4R 0010 (RPB4, group 1)
|
|
* INT4 is on RB4, which nothing drives, and counts none of RB5's edges; with INT4R 0000
|
|
* (RPA0, also the reset value) it counts its five rising edges of nine.
|
|
*/
|
|
REG(ANSELA, CLR) = 1u << 0;
|
|
SFR(INT4R) = 2;
|
|
REG(INTCON, SET) = 1u << 4; /* INT4EP: rising */
|
|
REG(IPC4, SET) = 2u << 26; /* vector 19: IPC4<28:26> */
|
|
REG(TRISB, CLR) = 1u << 5;
|
|
REG(LATB, CLR) = 1u << 5;
|
|
REG(IFS0, CLR) = 1u << 23;
|
|
REG(IEC0, SET) = 1u << 23;
|
|
for (int pass = 0; pass < 2; pass++) {
|
|
if (pass) {
|
|
elsewhere = int4_hits;
|
|
REG(LATB, CLR) = 1u << 5;
|
|
wait_counts(20000);
|
|
SFR(INT4R) = 0;
|
|
}
|
|
for (int i = 0; i < 9; i++) {
|
|
if (i & 1) {
|
|
REG(LATB, CLR) = 1u << 5;
|
|
} else {
|
|
REG(LATB, SET) = 1u << 5;
|
|
}
|
|
wait_counts(20000);
|
|
}
|
|
}
|
|
puts_("INT4 via RPB4 rising=");
|
|
put_dec(elsewhere);
|
|
puts_(" via RPA0 rising=");
|
|
put_dec(int4_hits - elsewhere);
|
|
puts_("\n");
|
|
|
|
/* Change notice on port B: RB11 enabled, RB10 drives it through the jumper. */
|
|
REG(TRISB, CLR) = 1u << 10;
|
|
REG(LATB, CLR) = 1u << 10;
|
|
SFR(CNCONB) = 1u << 15; /* ON, mismatch mode */
|
|
SFR(CNENB) = 1u << 11;
|
|
(void)SFR(PORTB); /* latch the inputs */
|
|
REG(IPC8, SET) = 2u << 18; /* vector 34: IPC8<20:18> */
|
|
REG(IFS1, CLR) = 1u << 14;
|
|
REG(IEC1, SET) = 1u << 14;
|
|
for (int i = 0; i < 4; i++) {
|
|
REG(LATB, INV) = 1u << 10;
|
|
wait_counts(20000);
|
|
}
|
|
REG(IEC1, CLR) = 1u << 14;
|
|
puts_("CN port B RB11 changes=");
|
|
put_dec(cn_hits);
|
|
puts_("\n");
|
|
|
|
/* RB2 (AN4) held high by the runner: analog at reset, it reads 0 (12.3.4). */
|
|
held = (SFR(PORTB) >> 2) & 1;
|
|
REG(ANSELB, CLR) = 1u << 2;
|
|
digital = (SFR(PORTB) >> 2) & 1;
|
|
/* RB4 with nothing on it, then with its pull-up. */
|
|
nopull = (SFR(PORTB) >> 4) & 1;
|
|
REG(CNPUB, SET) = 1u << 4;
|
|
pullup = (SFR(PORTB) >> 4) & 1;
|
|
puts_("RB2 analog=");
|
|
put_dec(held);
|
|
puts_(" digital=");
|
|
put_dec(digital);
|
|
puts_(" RB4 nopull=");
|
|
put_dec(nopull);
|
|
puts_(" pullup=");
|
|
put_dec(pullup);
|
|
puts_("\n");
|
|
|
|
/* IOLOCK: set after the unlock; a write to RPB15R is then ignored. */
|
|
before = SFR(RPB15R);
|
|
unlock();
|
|
REG(CFGCON, 0) = SFR(CFGCON) | (1u << 13);
|
|
SFR(SYSKEY) = 0;
|
|
SFR(RPB15R) = 0;
|
|
after = SFR(RPB15R);
|
|
lock = (SFR(CFGCON) >> 13) & 1;
|
|
unlock();
|
|
SFR(CFGCON) = SFR(CFGCON) & ~(1u << 13);
|
|
SFR(SYSKEY) = 0;
|
|
unlocked = !((SFR(CFGCON) >> 13) & 1);
|
|
puts_("IOLOCK=");
|
|
put_dec(lock);
|
|
puts_(" RPB15R kept=");
|
|
put_dec(before == 5 && after == 5);
|
|
puts_(" cleared=");
|
|
put_dec(unlocked);
|
|
puts_("\nONCE DONE\n");
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
uint32_t next;
|
|
int moved = 0;
|
|
|
|
/* UART1 on RPB3; 115200 at PBCLK 40 MHz with BRGH: 40e6 / (4 * 115200) - 1 = 86. */
|
|
REG(ANSELB, CLR) = (1u << 3) | (1u << 13) | (1u << 14) | (1u << 15);
|
|
SFR(RPB3R) = 1; /* U1TX (group 1, 0001) */
|
|
SFR(U1BRG) = 86;
|
|
SFR(U1STA) = (1u << 10) | (1u << 12); /* UTXEN, URXEN */
|
|
SFR(U1MODE) = (1u << 15) | (1u << 3); /* ON, BRGH, 8N1 */
|
|
puts_("PIC32MX1/2 bare-metal: start\n");
|
|
puts_("DEVID=");
|
|
put_hex(SFR(DEVID));
|
|
puts_(" OSCCON=");
|
|
put_hex(SFR(OSCCON));
|
|
puts_(" RPB3R=");
|
|
put_dec(SFR(RPB3R));
|
|
puts_("\n");
|
|
|
|
REG(TRISB, CLR) = (1u << 13) | (1u << 14) | (1u << 8);
|
|
|
|
/* OC1: PWM (OCM 110) on Timer3 (OCTSEL), 1 kHz: PBCLK 40 MHz / 8 / 5000, OC1RS 1250. */
|
|
SFR(RPB7R) = 5; /* OC1 (group 1, 0101) */
|
|
SFR(T3CON) = 0;
|
|
SFR(TMR3) = 0;
|
|
SFR(PR3) = 4999;
|
|
SFR(OC1RS) = 1250;
|
|
SFR(OC1R) = 1250;
|
|
SFR(OC1CON) = (1u << 15) | (1u << 3) | 6;
|
|
SFR(T3CON) = (1u << 15) | (3u << 4); /* ON, 1:8 */
|
|
|
|
/* Multi-vector mode; the core timer at priority 7 (IPC0<4:2>), Timer2 (vector 8) at 3. */
|
|
REG(INTCON, SET) = 1u << 12;
|
|
SFR(IPC0) = 7u << 2;
|
|
SFR(IPC2) = 3u << 2;
|
|
ct_next = mfc0_count() + 20000;
|
|
mtc0_compare(ct_next);
|
|
SFR(T2CON) = 0;
|
|
SFR(TMR2) = 0;
|
|
SFR(PR2) = 19999;
|
|
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 += 10000;
|
|
while ((int32_t)(mfc0_count() - next) < 0) {
|
|
}
|
|
REG(LATB, INV) = 1u << 13; /* RB13 */
|
|
if (ct_ticks >= 100 && !moved) {
|
|
/* OC1 moves from RPB7 to RPB15 (both group 1): RB7 is a port pin again. */
|
|
SFR(RPB7R) = 0;
|
|
SFR(RPB15R) = 5;
|
|
moved = 1;
|
|
}
|
|
if (ct_ticks == 200) {
|
|
puts_("CT ticks=200 T2 ticks=");
|
|
put_dec(t2_ticks);
|
|
puts_(" OC1 on RPB15R=");
|
|
put_dec(SFR(RPB15R));
|
|
puts_("\nDONE\n");
|
|
ct_ticks++; /* say it once */
|
|
once();
|
|
}
|
|
}
|
|
}
|