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]>
312 lines
8.5 KiB
C
312 lines
8.5 KiB
C
/*
|
|
* 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 <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 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();
|
|
}
|
|
}
|
|
}
|