Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ FLASHMEM void FreeKernalBin()
#endif
}

void FnetLoopPollStart(); //Teensy.ino

FLASHMEM void InitHndlr_KERNALReplace_PreStart() //not called automatically via init; called earlier, manually, from KERNALPreStart()
{
#ifdef Fab04_GlobalKernalReplace
Expand Down Expand Up @@ -400,6 +402,7 @@ FLASHMEM void InitHndlr_KERNALReplace_PreStart() //not called automatically via
#else
fBusSnoop = KernalCheckArm();
#endif
FnetLoopPollStart(); //Ethernet poll off the PIT timer from now on (see FnetLoopPollService in Teensy.ino)
}

FLASHMEM void InitHndlr_KernalReplace()
Expand Down
16 changes: 16 additions & 0 deletions Source/Teensy/MinimalBoot/Common/IO_Handlers/Swift_ATcommands.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,11 @@ FLASHMEM ATRespCode AT_Hook(char* CmdArg)
}


#ifdef Fab04_GlobalKernalReplace
FLASHMEM ATRespCode ProcessATCommandInner() //wrapped by ProcessATCommand() below
#else
FLASHMEM ATRespCode ProcessATCommand()
#endif
{
char* CmdMsg = TxMsg; //local pointer for manipulation
stcATCommand ATCommands[] =
Expand Down Expand Up @@ -466,3 +470,15 @@ FLASHMEM ATRespCode ProcessATCommand()
AddToPETSCIIStrToRxQueueLN(TxMsg);
return ATRC_ERROR;
}

#ifdef Fab04_GlobalKernalReplace
extern volatile bool FnetLoopPoll;
FLASHMEM ATRespCode ProcessATCommand()
{ //SwiftLink can run while the KERNAL is replaced: connect/DHCP/DNS block in here waiting for the stack, which then needs
//its PIT poll timer (masked while the poll runs from loop(), see FnetLoopPollService in Teensy.ino)
if (FnetLoopPoll) NVIC_ENABLE_IRQ(IRQ_PIT);
ATRespCode r = ProcessATCommandInner();
if (FnetLoopPoll) NVIC_DISABLE_IRQ(IRQ_PIT);
return r;
}
#endif
43 changes: 43 additions & 0 deletions Source/Teensy/Teensy.ino
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ uint8_t RAM_Image[RAM_ImageSize]; //Main RAM1 file storage buffer
volatile uint8_t BtnPressed = false;
volatile uint8_t EmulateVicCycles = false;
uint8_t CurrentIOHandler = IOH_None;
#ifdef Fab04_KernalReplace
volatile bool FnetLoopPoll = false; //Ethernet stack polled from loop(), PIT timer masked (see FnetLoopPollService)
volatile bool FnetPollHold = false; //set by Kernal Replace PreStart, before CurrentIOHandler changes; cleared with the main menu
void FnetLoopPollService();
FLASHMEM void FnetLoopPollStart()
{ //start polling from loop() right away: loop() only sees the new handler ~0.4 s after the launch, and late phi2 interrupts in that time crashed programs
FnetPollHold = true;
if (fnet_netif_get_default() != 0 && !FnetLoopPoll) { NVIC_DISABLE_IRQ(IRQ_PIT); FnetLoopPoll = true; }
}
#endif
StructMenuItem *DriveDirMenu = NULL;
uint16_t NumDrvDirMenuItems = 0;
char DriveDirPath[MaxPathLength];
Expand Down Expand Up @@ -377,6 +387,10 @@ void loop()
if (IO1[rwRegPwrUpDefaults2] & rpud2TRContEnabled)
if (USBHostSerial.available()) ServiceSerial(&USBHostSerial);

#ifdef Fab04_KernalReplace
FnetLoopPollService();
#endif

#ifdef FeatTCPListen
if (NetListenEnable)
{
Expand All @@ -399,11 +413,40 @@ void loop()
if (IOHandler[CurrentIOHandler]->PollingHndlr != NULL) IOHandler[CurrentIOHandler]->PollingHndlr();
}

#ifdef Fab04_KernalReplace
FLASHMEM void FnetLoopPollService()
{ //While Kernal Replace serves the KERNAL, run the Ethernet stack poll from here instead of NativeEthernet's 1ms PIT timer ISR:
//with the poll in the PIT interrupt, ~39 phi2 interrupts/s were late (>1.1us) even with the GPIO interrupt at a higher priority,
//and a late one on the HIRAM sense after a $01 write corrupts the read; called from loop() they are down to about one every 2s.
//(The PIT is only used by that timer, except for ASID, which masks it itself.)
static uint32_t lastPollUs = 0;
bool want = FnetPollHold || (CurrentIOHandler == IOH_KernalReplace)
#ifdef Fab04_GlobalKernalReplace
|| (fKernRepl != NULL)
#endif
;
want = want && (fnet_netif_get_default() != 0); //stack initialized
if (want != FnetLoopPoll)
{
FnetLoopPoll = want;
if (want) NVIC_DISABLE_IRQ(IRQ_PIT); else NVIC_ENABLE_IRQ(IRQ_PIT);
}
if (FnetLoopPoll && micros() - lastPollUs >= 1000)
{
lastPollUs = micros();
fnet_poll();
}
}
#endif

FLASHMEM void SetUpMainMenuROM()
{
PendingfBusSnoop = fBusSnoop = NULL;
#ifdef Fab04_GlobalKernalReplace
fKernRepl = NULL;
#endif
#ifdef Fab04_KernalReplace
FnetPollHold = false;
#endif
SetDMADeassert;
SetIRQDeassert;
Expand Down
4 changes: 4 additions & 0 deletions docs/Architecture/Constraints.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ Beyond priority, Ethernet/PIT interrupts are fully **disabled** (not just deprio
- **Firmware self-flash** — `Flash/FXUtil.cpp:175-176` disables both, and goes further by fully `detachInterrupt()`-ing the button and PHI2 pins entirely just above (lines 173-174) — there's no valid handler to service PHI2 while live firmware is being overwritten.
- **ASID (MIDI SID) playback** — `IOH_ASID.c:579-580` disables both during ASID streaming, which has its own tight audio-rate timing requirement.

The priority alone does not keep the Ethernet stack out of `isrPHI2`'s way. NativeEthernet runs the whole FNET stack (`fnet_poll()`) from a 1 ms `IntervalTimer` (PIT) ISR, and with the stack initialized (no cable needed) about 39 PHI2 interrupts per second came in late (>1.1 µs), with the GPIO interrupt at the higher priority and with `IRQ_ENET` masked. A handler that serves bytes on reads it has to sense first (Kernal Replace's HIRAM sense) gets corrupt reads, NMIs or freezes from those. So while **Kernal Replace** serves the KERNAL (its Special IO, or `fKernRepl` with `Fab04_GlobalKernalReplace`), `IRQ_PIT` is masked and `FnetLoopPollService()` (`Teensy.ino`) calls `fnet_poll()` from `loop()` every ms instead: late PHI2 interrupts drop to about one every 2 s. Kernal Replace starts this in `InitHndlr_KERNALReplace_PreStart()` (`FnetLoopPollStart()`), since `loop()` sees the new handler only ~0.4 s after a launch; `SetUpMainMenuROM()` ends it. Two consequences:
- TCP is serviced only as often as `loop()` comes round meanwhile, so a `PollingHndlr` that blocks also stalls the network.
- Code that blocks waiting for the stack (`connect`, DHCP, DNS) needs the PIT poll back: `ProcessATCommand()` (`Swift_ATcommands.c`) unmasks `IRQ_PIT` while an AT command runs (SwiftLink under `Fab04_GlobalKernalReplace`). New blocking network calls that can run while the KERNAL is replaced need the same.

## The main `loop()` is single-threaded — a blocking call anywhere in it stalls every other main-loop-driven task

`Teensy.ino`'s `loop()` dispatches `ServiceSerial()`/`ProcessCommand()` (incoming serial/network
Expand Down
Loading