Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Crocker <dcrocker@eschertech.com>2020-08-28 12:25:34 +0300
committerDavid Crocker <dcrocker@eschertech.com>2020-08-28 12:25:34 +0300
commitfacd6fa3f9979b7fb8d7eec1839b3ad3dfa167e7 (patch)
treeb379c1d26ff220c52ad9129187c560c9fffa999b /src/Hardware
parent05c995f2968aabaa3fa8582fb49ab0574e492342 (diff)
Various
Changed layout of User Page to accommodate ADC calibration as well as software reset data. Added new module NonVolatileMemory to manage this. Removed RADDS configuration so that we don't need to support SAM3XA flash memory. Increased number of stack words stored in software reset data. Moved exception handlers out of Tasks.cpp to separate module ExceptionHandlers. Changed number of decimal places reported in spindle RPMs from default (7) to 1. Moved some low-level functions from CanInterface to CanDriver and started preparing CanDriver to support bith CAN interfaces. Added L parameter (calibration factor) to laser filament monitor configuration Renamed GCodeChannel::USBchan back to USB for backwards compatibility
Diffstat (limited to 'src/Hardware')
-rw-r--r--src/Hardware/ExceptionHandlers.cpp283
-rw-r--r--src/Hardware/ExceptionHandlers.h15
-rw-r--r--src/Hardware/NonVolatileMemory.cpp171
-rw-r--r--src/Hardware/NonVolatileMemory.h59
-rw-r--r--src/Hardware/SAME70/CanDriver.cpp91
-rw-r--r--src/Hardware/SAME70/CanDriver.h14
-rw-r--r--src/Hardware/SoftwareReset.cpp93
-rw-r--r--src/Hardware/SoftwareReset.h85
8 files changed, 804 insertions, 7 deletions
diff --git a/src/Hardware/ExceptionHandlers.cpp b/src/Hardware/ExceptionHandlers.cpp
new file mode 100644
index 00000000..d73492df
--- /dev/null
+++ b/src/Hardware/ExceptionHandlers.cpp
@@ -0,0 +1,283 @@
+/*
+ * CrashHandlers.cpp
+ *
+ * Created on: 24 Aug 2020
+ * Author: David
+ */
+
+#include "ExceptionHandlers.h"
+#include <RepRap.h>
+#include <Platform.h>
+#include <Hardware/NonVolatileMemory.h>
+#include <Cache.h>
+
+// Perform a software reset. 'stk' points to the program counter on the stack if the cause is an exception, otherwise it is nullptr.
+void SoftwareReset(uint16_t reason, const uint32_t *stk) noexcept
+{
+ cpu_irq_disable(); // disable interrupts before we call any flash functions. We don't enable them again.
+ WatchdogReset(); // kick the watchdog
+
+#if SAM4E || SAME70
+ rswdt_restart(RSWDT); // kick the secondary watchdog
+#endif
+
+ Cache::Disable();
+
+#if USE_MPU
+ //TODO set the flash memory to strongly-ordered or device instead
+ ARM_MPU_Disable(); // disable the MPU
+#endif
+
+ if (reason == (uint16_t)SoftwareResetReason::erase)
+ {
+#if SAME5x
+ //TODO invalidate flash so the USB bootloader runs
+#else
+ EraseAndReset();
+#endif
+ }
+ else
+ {
+ if (reason != (uint16_t)SoftwareResetReason::user)
+ {
+ if (SERIAL_MAIN_DEVICE.canWrite() == 0)
+ {
+ reason |= (uint16_t)SoftwareResetReason::inUsbOutput; // if we are resetting because we are stuck in a Spin function, record whether we are trying to send to USB
+ }
+
+#ifdef SERIAL_AUX_DEVICE
+ if (SERIAL_AUX_DEVICE.canWrite() == 0
+# ifdef SERIAL_AUX2_DEVICE
+ || SERIAL_AUX2_DEVICE.canWrite() == 0
+# endif
+ )
+ {
+ reason |= (uint16_t)SoftwareResetReason::inAuxOutput; // if we are resetting because we are stuck in a Spin function, record whether we are trying to send to aux
+ }
+#endif
+ }
+ reason |= (uint8_t)reprap.GetSpinningModule();
+ if (reprap.GetPlatform().WasDeliberateError())
+ {
+ reason |= (uint16_t)SoftwareResetReason::deliberate;
+ }
+
+ // Record the reason for the software reset
+ NonVolatileMemory mem;
+ SoftwareResetData * const srd = mem.AllocateResetDataSlot();
+
+#if defined(__LPC17xx__)
+ srd->Populate(reason, (uint32_t)realTime, stk);
+#else
+ srd->Populate(reason, (uint32_t)reprap.GetPlatform().GetDateTime(), stk);
+#endif
+ mem.EnsureWritten();
+ }
+
+#if defined(__LPC17xx__)
+ LPC_SYSCTL->RSID = 0x3F; // Clear bits in reset reasons stored in RSID
+#elif !SAME5x
+ RSTC->RSTC_MR = RSTC_MR_KEY_PASSWD; // ignore any signal on the NRST pin for now so that the reset reason will show as Software
+#endif
+ Reset();
+ for(;;) {}
+}
+
+// Exception handlers
+// By default the Usage Fault, Bus Fault and Memory Management fault handlers are not enabled,
+// so they escalate to a Hard Fault and we don't need to provide separate exception handlers for them.
+extern "C" [[noreturn]] void hardFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+{
+ SoftwareReset((uint16_t)SoftwareResetReason::hardFault, pulFaultStackAddress + 5);
+}
+
+// The fault handler implementation calls a function called hardFaultDispatcher()
+extern "C" [[noreturn]] void HardFault_Handler() noexcept __attribute__((naked));
+void HardFault_Handler() noexcept
+{
+ __asm volatile
+ (
+ " tst lr, #4 \n" /* test bit 2 of the EXC_RETURN in LR to determine which stack was in use */
+ " ite eq \n" /* load the appropriate stack pointer into R0 */
+ " mrseq r0, msp \n"
+ " mrsne r0, psp \n"
+ " ldr r2, handler_hf_address_const \n"
+ " bx r2 \n"
+ " handler_hf_address_const: .word hardFaultDispatcher \n"
+ );
+}
+
+#if USE_MPU
+
+extern "C" [[noreturn]] void memManageDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+{
+ SoftwareReset((uint16_t)SoftwareResetReason::memFault, pulFaultStackAddress + 5);
+}
+
+// The fault handler implementation calls a function called memManageDispatcher()
+extern "C" [[noreturn]] void MemManage_Handler() noexcept __attribute__((naked));
+void MemManage_Handler() noexcept
+{
+ __asm volatile
+ (
+ " tst lr, #4 \n" /* test bit 2 of the EXC_RETURN in LR to determine which stack was in use */
+ " ite eq \n" /* load the appropriate stack pointer into R0 */
+ " mrseq r0, msp \n"
+ " mrsne r0, psp \n"
+ " ldr r2, handler_mf_address_const \n"
+ " bx r2 \n"
+ " handler_mf_address_const: .word memManageDispatcher \n"
+ );
+}
+
+#endif
+
+extern "C" [[noreturn]] void wdtFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+{
+ SoftwareReset((uint16_t)SoftwareResetReason::wdtFault, pulFaultStackAddress + 5);
+}
+
+#ifdef __LPC17xx__
+extern "C" [[noreturn]] void WDT_IRQHandler() noexcept __attribute__((naked));
+void WDT_IRQHandler() noexcept
+{
+ LPC_WDT->MOD &=~((uint32_t)(1<<2)); //SD::clear timout flag before resetting to prevent the Smoothie bootloader going into DFU mode
+#else
+extern "C" [[noreturn]] void WDT_Handler() noexcept __attribute__((naked));
+void WDT_Handler() noexcept
+{
+#endif
+ __asm volatile
+ (
+ " tst lr, #4 \n" /* test bit 2 of the EXC_RETURN in LR to determine which stack was in use */
+ " ite eq \n" /* load the appropriate stack pointer into R0 */
+ " mrseq r0, msp \n"
+ " mrsne r0, psp \n"
+ " ldr r2, handler_wdt_address_const \n"
+ " bx r2 \n"
+ " handler_wdt_address_const: .word wdtFaultDispatcher \n"
+ );
+}
+
+extern "C" [[noreturn]] void otherFaultDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+{
+ SoftwareReset((uint16_t)SoftwareResetReason::otherFault, pulFaultStackAddress + 5);
+}
+
+// 2017-05-25: A user is getting 'otherFault' reports, so now we do a stack dump for those too.
+// The fault handler implementation calls a function called otherFaultDispatcher()
+extern "C" [[noreturn]] void OtherFault_Handler() noexcept __attribute__((naked));
+void OtherFault_Handler() noexcept
+{
+ __asm volatile
+ (
+ " tst lr, #4 \n" /* test bit 2 of the EXC_RETURN in LR to determine which stack was in use */
+ " ite eq \n" /* load the appropriate stack pointer into R0 */
+ " mrseq r0, msp \n"
+ " mrsne r0, psp \n"
+ " ldr r2, handler_oflt_address_const \n"
+ " bx r2 \n"
+ " handler_oflt_address_const: .word otherFaultDispatcher \n"
+ );
+}
+
+// We could set up the following fault handlers to retrieve the program counter in the same way as for a Hard Fault,
+// however these exceptions are unlikely to occur, so for now we just report the exception type.
+extern "C" [[noreturn]] void NMI_Handler () noexcept { SoftwareReset((uint16_t)SoftwareResetReason::NMI); }
+extern "C" [[noreturn]] void UsageFault_Handler () noexcept { SoftwareReset((uint16_t)SoftwareResetReason::usageFault); }
+
+extern "C" [[noreturn]] void DebugMon_Handler () noexcept __attribute__ ((alias("OtherFault_Handler")));
+
+// FreeRTOS hooks that we need to provide
+extern "C" [[noreturn]] void stackOverflowDispatcher(const uint32_t *pulFaultStackAddress, char* pcTaskName) noexcept
+{
+ SoftwareReset((uint16_t)SoftwareResetReason::stackOverflow, pulFaultStackAddress);
+}
+
+extern "C" [[noreturn]] void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName) noexcept __attribute((naked));
+void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName) noexcept
+{
+ // r0 = pxTask, r1 = pxTaskName
+ __asm volatile
+ (
+ " push {r0, r1, lr} \n" /* save parameters and call address on the stack */
+ " mov r0, sp \n"
+ " ldr r2, handler_sovf_address_const \n"
+ " bx r2 \n"
+ " handler_sovf_address_const: .word stackOverflowDispatcher \n"
+ );
+}
+
+extern "C" [[noreturn]] void assertCalledDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+{
+ SoftwareReset((uint16_t)SoftwareResetReason::assertCalled, pulFaultStackAddress);
+}
+
+extern "C" [[noreturn]] void vAssertCalled(uint32_t line, const char *file) noexcept __attribute((naked));
+void vAssertCalled(uint32_t line, const char *file) noexcept
+{
+#if false
+ debugPrintf("ASSERTION FAILED IN %s on LINE %d\n", file, line);
+ SERIAL_MAIN_DEVICE.flush();
+#endif
+ __asm volatile
+ (
+ " push {r0, r1, lr} \n" /* save parameters and call address */
+ " mov r0, sp \n"
+ " ldr r2, handler_asrt_address_const \n"
+ " bx r2 \n"
+ " handler_asrt_address_const: .word assertCalledDispatcher \n"
+ );
+}
+
+#ifdef __LPC17xx__
+[[noreturn]] void applicationMallocFailedCalledDispatcher(const uint32_t *pulFaultStackAddress) noexcept
+{
+ reprap.SoftwareReset((uint16_t)SoftwareResetReason::assertCalled, pulFaultStackAddress);
+}
+
+[[noreturn]] extern "C" void vApplicationMallocFailedHook() noexcept __attribute((naked));
+void vApplicationMallocFailedHook() noexcept
+{
+ __asm volatile
+ (
+ " push {r0, r1, lr} \n" /* save parameters and call address */
+ " mov r0, sp \n"
+ " ldr r2, handler_amf_address_const \n"
+ " bx r2 \n"
+ " handler_amf_address_const: .word applicationMallocFailedCalledDispatcher \n"
+ );
+}
+#endif
+
+namespace std
+{
+ // We need to define this function in order to use lambda functions with captures
+ [[noreturn]] void __throw_bad_function_call() noexcept { vAssertCalled(__LINE__, __FILE__); }
+}
+
+// The default terminate handler pulls in sprintf and lots of other functions, which makes the binary too large. So we replace it.
+[[noreturn]] void Terminate() noexcept
+{
+ register const uint32_t * stack_ptr asm ("sp");
+ SoftwareReset((uint16_t)SoftwareResetReason::terminateCalled, stack_ptr);
+}
+
+namespace __cxxabiv1
+{
+ std::terminate_handler __terminate_handler = Terminate;
+}
+
+extern "C" [[noreturn]] void __cxa_pure_virtual() noexcept
+{
+ register const uint32_t * stack_ptr asm ("sp");
+ SoftwareReset((uint16_t)SoftwareResetReason::pureVirtual, stack_ptr);
+}
+
+extern "C" [[noreturn]] void __cxa_deleted_virtual() noexcept
+{
+ register const uint32_t * stack_ptr asm ("sp");
+ SoftwareReset((uint16_t)SoftwareResetReason::deletedVirtual, stack_ptr);
+}
+
+// End
diff --git a/src/Hardware/ExceptionHandlers.h b/src/Hardware/ExceptionHandlers.h
new file mode 100644
index 00000000..a5082add
--- /dev/null
+++ b/src/Hardware/ExceptionHandlers.h
@@ -0,0 +1,15 @@
+/*
+ * CrashHandlers.h
+ *
+ * Created on: 24 Aug 2020
+ * Author: David
+ */
+
+#ifndef SRC_HARDWARE_EXCEPTIONHANDLERS_H_
+#define SRC_HARDWARE_EXCEPTIONHANDLERS_H_
+
+#include <cstdint>
+
+[[noreturn]] void SoftwareReset(uint16_t reason, const uint32_t *stk = nullptr) noexcept;
+
+#endif /* SRC_HARDWARE_EXCEPTIONHANDLERS_H_ */
diff --git a/src/Hardware/NonVolatileMemory.cpp b/src/Hardware/NonVolatileMemory.cpp
new file mode 100644
index 00000000..1e08d6d2
--- /dev/null
+++ b/src/Hardware/NonVolatileMemory.cpp
@@ -0,0 +1,171 @@
+/*
+ * NonVolatileMemory.cpp
+ *
+ * Created on: 24 Aug 2020
+ * Author: David
+ */
+
+#include "NonVolatileMemory.h"
+
+#if SAM4E || SAM4S || SAME70
+# include <Cache.h>
+# include <flash_efc.h>
+#endif
+
+NonVolatileMemory::NonVolatileMemory() noexcept : state(NvmState::notRead)
+{
+}
+
+void NonVolatileMemory::EnsureRead() noexcept
+{
+ if (state == NvmState::notRead)
+ {
+#if SAME5x
+ memcpy(&buffer, reinterpret_cast<const void *>(SEEPROM_ADDR), sizeof(buffer));
+#elif defined(__LPC17xx__)
+ qq; //TODO
+#elif SAM4E || SAM4S || SAME70
+ // Work around bug in ASF flash library: flash_read_user_signature calls a RAMFUNC without disabling interrupts first.
+ // This caused a crash (watchdog timeout) sometimes if we run M122 while a print is in progress
+ const irqflags_t flags = cpu_irq_save();
+ Cache::Disable();
+ flash_read_user_signature(reinterpret_cast<uint32_t*>(&buffer), sizeof(buffer)/sizeof(uint32_t));
+ Cache::Enable();
+ cpu_irq_restore(flags);
+#else
+# error Unsupported processor
+#endif
+ if (buffer.magic != NVM::MagicValue)
+ {
+ memset(&buffer, 0xFF, sizeof(buffer));
+ buffer.magic = NVM::MagicValue;
+ state = NvmState::eraseAndWriteNeeded;
+ }
+ else
+ {
+ state = NvmState::clean;
+ }
+ }
+}
+
+void NonVolatileMemory::EnsureWritten() noexcept
+{
+#if SAME5x
+ if (state >= NvmState::writeNeeded)
+ {
+ while (NVMCTRL->SEESTAT.bit.BUSY) { }
+ memcpy(reinterpret_cast<uint8_t*>(SEEPROM_ADDR), &buffer, sizeof(buffer));
+ state = NvmState::clean;
+ }
+#else
+ if (state == NvmState::eraseAndWriteNeeded)
+ {
+ // Erase the page
+# if SAM4E || SAM4S || SAME70
+ flash_erase_user_signature();
+# elif defined(__LPC17xx__)
+ LPC_EraseSoftwareResetDataSlots(); // erase the last flash sector
+# endif
+ state = NvmState::writeNeeded;
+ }
+
+ if (state == NvmState::writeNeeded)
+ {
+# if SAM4E || SAM4S || SAME70
+ flash_write_user_signature(&buffer, sizeof(buffer)/sizeof(uint32_t));
+# elif defined(__LPC17xx__)
+ LPC_WriteSoftwareResetData(slot, buffer, sizeof(buffer));
+# else
+# error Unsupported processor
+# endif
+ state = NvmState::clean;
+ }
+#endif
+}
+
+SoftwareResetData* NonVolatileMemory::GetLastWrittenResetData(unsigned int &slot) noexcept
+{
+ EnsureRead();
+ for (unsigned int i = NumberOfResetDataSlots; i != 0; )
+ {
+ --i;
+ if (buffer.resetData[i].IsValid())
+ {
+ slot = i;
+ return &buffer.resetData[i];
+ }
+ }
+ return nullptr;
+}
+
+SoftwareResetData* NonVolatileMemory::AllocateResetDataSlot() noexcept
+{
+ EnsureRead();
+ for (unsigned int i = 0; i < NumberOfResetDataSlots; ++i)
+ {
+ if (buffer.resetData[i].IsVacant())
+ {
+ state = NvmState::writeNeeded; // assume the caller will write to the allocated slot
+ return &buffer.resetData[i];
+ }
+ }
+
+ // All slots are full, so clear them out and start again
+ for (unsigned int i = 0; i < NumberOfResetDataSlots; ++i)
+ {
+ buffer.resetData[i].Clear();
+ }
+ state = NvmState::eraseAndWriteNeeded;
+ return &buffer.resetData[0];
+}
+
+int8_t NonVolatileMemory::GetThermistorLowCalibration(unsigned int inputNumber) noexcept
+{
+ return GetThermistorCalibration(inputNumber, buffer.thermistorLowCalibration);
+}
+
+int8_t NonVolatileMemory::GetThermistorHighCalibration(unsigned int inputNumber) noexcept
+{
+ return GetThermistorCalibration(inputNumber, buffer.thermistorHighCalibration);
+}
+
+void NonVolatileMemory::SetThermistorLowCalibration(unsigned int inputNumber, int8_t val) noexcept
+{
+ SetThermistorCalibration(inputNumber, val, buffer.thermistorLowCalibration);
+}
+
+void NonVolatileMemory::SetThermistorHighCalibration(unsigned int inputNumber, int8_t val) noexcept
+{
+ SetThermistorCalibration(inputNumber, val, buffer.thermistorHighCalibration);
+}
+
+int8_t NonVolatileMemory::GetThermistorCalibration(unsigned int inputNumber, uint8_t *calibArray) noexcept
+{
+ EnsureRead();
+ return (inputNumber >= MaxCalibratedThermistors || calibArray[inputNumber] == 0xFF) ? 0 : calibArray[inputNumber] - 0x7F;
+}
+
+void NonVolatileMemory::SetThermistorCalibration(unsigned int inputNumber, int8_t val, uint8_t *calibArray) noexcept
+{
+ if (inputNumber < MaxCalibratedThermistors)
+ {
+ EnsureRead();
+ const uint8_t oldVal = calibArray[inputNumber];
+ const uint8_t newVal = val + 0x7F;
+ if (oldVal != newVal)
+ {
+ // If we are only changing 1 bits to 0 then we don't need to erase
+ calibArray[inputNumber] = newVal;
+ if ((newVal & ~oldVal) != 0)
+ {
+ state = NvmState::eraseAndWriteNeeded;
+ }
+ else if (state == NvmState::clean)
+ {
+ state = NvmState::writeNeeded;
+ }
+ }
+ }
+}
+
+// End
diff --git a/src/Hardware/NonVolatileMemory.h b/src/Hardware/NonVolatileMemory.h
new file mode 100644
index 00000000..d5175bbb
--- /dev/null
+++ b/src/Hardware/NonVolatileMemory.h
@@ -0,0 +1,59 @@
+/*
+ * NonVolatileMemory.h
+ *
+ * Class to manage an area of flash memory that we use to store data that persists over a reset
+ * Created on: 24 Aug 2020
+ * Author: David
+ */
+
+#ifndef SRC_HARDWARE_NONVOLATILEMEMORY_H_
+#define SRC_HARDWARE_NONVOLATILEMEMORY_H_
+
+#include <Hardware/SoftwareReset.h>
+
+// This class manages nonvolatile settings that are specific to the board, and the software reset data that is stored by the crash handler.
+// On most Duets there is a 512-byte User Page that we use for this.
+// The SAMC21 and SAME5x processors already store various data in the user page, however both those processor families support EEPROM emulation so we use 512 bytes of that instead.
+
+class NonVolatileMemory
+{
+public:
+ NonVolatileMemory() noexcept;
+
+ void EnsureWritten() noexcept;
+ SoftwareResetData *GetLastWrittenResetData(unsigned int &slot) noexcept;
+ SoftwareResetData *AllocateResetDataSlot() noexcept;
+ int8_t GetThermistorLowCalibration(unsigned int inputNumber) noexcept;
+ int8_t GetThermistorHighCalibration(unsigned int inputNumber) noexcept;
+ void SetThermistorLowCalibration(unsigned int inputNumber, int8_t val) noexcept;
+ void SetThermistorHighCalibration(unsigned int inputNumber, int8_t val) noexcept;
+
+ static constexpr unsigned int NumberOfResetDataSlots = 3;
+ static constexpr unsigned int MaxCalibratedThermistors = 8;
+
+private:
+ void EnsureRead() noexcept;
+ int8_t GetThermistorCalibration(unsigned int inputNumber, uint8_t *calibArray) noexcept;
+ void SetThermistorCalibration(unsigned int inputNumber, int8_t val, uint8_t *calibArray) noexcept;
+
+ struct NVM
+ {
+ uint16_t magic;
+ uint8_t thermistorLowCalibration[MaxCalibratedThermistors]; // currently used only by SAME70-based boards
+ uint8_t thermistorHighCalibration[MaxCalibratedThermistors]; // currently used only by SAME70-based boards
+ uint8_t spare[38];
+ // 56 bytes up to here
+ SoftwareResetData resetData[NumberOfResetDataSlots]; // 3 slots of 152 bytes each
+
+ static constexpr uint32_t MagicValue = 0x41E5;
+ };
+
+ static_assert(sizeof(NVM) == 512);
+
+ enum class NvmState : uint8_t { notRead, clean, writeNeeded, eraseAndWriteNeeded };
+
+ alignas(4) NVM buffer;
+ NvmState state;
+};
+
+#endif /* SRC_HARDWARE_NONVOLATILEMEMORY_H_ */
diff --git a/src/Hardware/SAME70/CanDriver.cpp b/src/Hardware/SAME70/CanDriver.cpp
index d9e75711..d91f1847 100644
--- a/src/Hardware/SAME70/CanDriver.cpp
+++ b/src/Hardware/SAME70/CanDriver.cpp
@@ -318,20 +318,17 @@ void mcan_init(mcan_module *const module_inst, Mcan *hw, struct mcan_config *con
/* Associate the software module instance with the hardware module */
module_inst->hw = hw;
+ module_inst->taskWaitingOnFifo[0] = module_inst->taskWaitingOnFifo[1] = nullptr;
pmc_disable_pck(PMC_PCK_5);
-#if 1 // dc42 use UPLL not PLLA as recommended in the documentation, so the MCAN clock is independent of the CPU clock frequency
+ // Use UPLL so the MCAN clock is independent of the CPU clock frequency
pmc_switch_pck_to_upllck(PMC_PCK_5, PMC_PCK_PRES(9)); // run PCLK5 at 48MHz
-#else
- pmc_switch_pck_to_pllack(PMC_PCK_5, PMC_PCK_PRES(9));
-#endif
pmc_enable_pck(PMC_PCK_5);
/* Enable peripheral clock */
_mcan_enable_peripheral_clock(module_inst);
-
/* Configuration Change Enable. */
hw->MCAN_CCCR |= MCAN_CCCR_CCE;
@@ -739,6 +736,90 @@ bool WaitForTxBufferFree(mcan_module *const module_inst, uint32_t whichTxBuffer,
return true;
}
+// Get a message from a FIFO with timeout. Return true if successful, false if we timed out
+bool GetMessageFromFifo(mcan_module *const module_inst, CanMessageBuffer *buf, unsigned int fifoNumber, uint32_t timeout) noexcept
+{
+ volatile uint32_t* const fifoRegisters = &(module_inst->hw->MCAN_RXF0S) + (4 * fifoNumber); // pointer to FIFO status register followed by FIFO acknowledge register
+ module_inst->taskWaitingOnFifo[fifoNumber] = TaskBase::GetCallerTaskHandle();
+ while (true)
+ {
+ const uint32_t status = fifoRegisters[0]; // get FIFO status
+ if ((status & MCAN_RXF0S_F0FL_Msk) != 0) // if there are any messages
+ {
+ const uint32_t getIndex = (status & MCAN_RXF0S_F0GI_Msk) >> MCAN_RXF0S_F0GI_Pos;
+ mcan_rx_element elem;
+ if (fifoNumber == 1)
+ {
+ mcan_get_rx_fifo_1_element(module_inst, &elem, getIndex); // copy the data (TODO use our own driver, avoid double copying)
+ }
+ else
+ {
+ mcan_get_rx_fifo_0_element(module_inst, &elem, getIndex); // copy the data (TODO use our own driver, avoid double copying)
+ }
+ fifoRegisters[1] = getIndex; // acknowledge it, release the FIFO entry
+
+ if (elem.R0.bit.XTD == 1 && elem.R0.bit.RTR != 1) // if extended address and not a remote frame
+ {
+ // Copy the message and accompanying data to our buffer
+ buf->id.SetReceivedId(elem.R0.bit.ID);
+ buf->dataLength = dlc2len[elem.R1.bit.DLC];
+ memcpy(buf->msg.raw, elem.data, buf->dataLength);
+ module_inst->taskWaitingOnFifo[fifoNumber] = nullptr;
+ return true;
+ }
+ }
+ else if (!TaskBase::Take(timeout))
+ {
+ module_inst->taskWaitingOnFifo[fifoNumber] = nullptr;
+ return false;
+ }
+ }
+}
+
+void GetLocalCanTiming(mcan_module *const module_inst, CanTiming& timing) noexcept
+{
+ const uint32_t nbtp = module_inst->hw->MCAN_NBTP;
+ const uint32_t tseg1 = (nbtp & MCAN_NBTP_NTSEG1_Msk) >> MCAN_NBTP_NTSEG1_Pos;
+ const uint32_t tseg2 = (nbtp & MCAN_NBTP_NTSEG2_Msk) >> MCAN_NBTP_NTSEG2_Pos;
+ const uint32_t jw = (nbtp & MCAN_NBTP_NSJW_Msk) >> MCAN_NBTP_NSJW_Pos;
+ const uint32_t brp = (nbtp & MCAN_NBTP_NBRP_Msk) >> MCAN_NBTP_NBRP_Pos;
+ timing.period = (tseg1 + tseg2 + 3) * (brp + 1);
+ timing.tseg1 = (tseg1 + 1) * (brp + 1);
+ timing.jumpWidth = (jw + 1) * (brp + 1);
+}
+
+void ChangeLocalCanTiming(mcan_module *const module_inst, const CanTiming& timing) noexcept
+{
+ // Sort out the bit timing
+ uint32_t period = timing.period;
+ uint32_t tseg1 = timing.tseg1;
+ uint32_t jumpWidth = timing.jumpWidth;
+ uint32_t prescaler = 1; // 48MHz main clock
+ uint32_t tseg2;
+
+ for (;;)
+ {
+ tseg2 = period - tseg1 - 1;
+ if (tseg1 <= 32 && tseg2 <= 16 && jumpWidth <= 16)
+ {
+ break;
+ }
+ prescaler <<= 1;
+ period >>= 1;
+ tseg1 >>= 1;
+ jumpWidth >>= 1;
+ }
+
+ //TODO stop transmissions in an orderly fashion, or postpone initialising CAN until we have the timing data
+ module_inst->hw->MCAN_CCCR |= MCAN_CCCR_CCE | MCAN_CCCR_INIT;
+ module_inst->hw->MCAN_NBTP = ((tseg1 - 1) << MCAN_NBTP_NTSEG1_Pos)
+ | ((tseg2 - 1) << MCAN_NBTP_NTSEG2_Pos)
+ | ((jumpWidth - 1) << MCAN_NBTP_NSJW_Pos)
+ | ((prescaler - 1) << MCAN_NBTP_NBRP_Pos);
+ module_inst->hw->MCAN_CCCR &= ~MCAN_CCCR_CCE;
+}
+
+
#endif // SUPPORT_CAN_EXPANSION
// End
diff --git a/src/Hardware/SAME70/CanDriver.h b/src/Hardware/SAME70/CanDriver.h
index ae2f5290..bd505233 100644
--- a/src/Hardware/SAME70/CanDriver.h
+++ b/src/Hardware/SAME70/CanDriver.h
@@ -42,7 +42,9 @@
#if SUPPORT_CAN_EXPANSION
-#include <compiler.h>
+#include <CanSettings.h>
+#include <CanMessageBuffer.h>
+#include <RTOSIface/RTOSIface.h>
#include <status_codes.h>
/** The value should be 8/12/16/20/24/32/48/64. */
@@ -529,8 +531,10 @@ enum mcan_nonmatching_frames_action {
* \note The fields of this structure should not be altered by the user
* application; they are reserved for module-internal use only.
*/
-struct mcan_module {
+struct mcan_module
+{
Mcan *hw;
+ TaskHandle taskWaitingOnFifo[2];
};
/**
@@ -692,6 +696,12 @@ status_code mcan_fd_send_ext_message_no_wait(mcan_module *const module_inst, uin
// Return true if we cancelled the pending transmission.
bool WaitForTxBufferFree(mcan_module *const module_inst, uint32_t whichTxBuffer, uint32_t maxWait) noexcept;
+// Get a message from a FIFO with timeout. Return true if successful, false if we timed out
+bool GetMessageFromFifo(mcan_module *const module_inst, CanMessageBuffer *buf, unsigned int fifoNumber, uint32_t timeout) noexcept;
+
+void GetLocalCanTiming(mcan_module *const module_inst, CanTiming& timing) noexcept;
+void ChangeLocalCanTiming(mcan_module *const module_inst, const CanTiming& timing) noexcept;
+
/**
* \brief Can read timestamp count value.
*
diff --git a/src/Hardware/SoftwareReset.cpp b/src/Hardware/SoftwareReset.cpp
new file mode 100644
index 00000000..9ed16c34
--- /dev/null
+++ b/src/Hardware/SoftwareReset.cpp
@@ -0,0 +1,93 @@
+/*
+ * SoftwareReset.cpp
+ *
+ * Created on: 15 Nov 2019
+ * Author: David
+ */
+
+#include "SoftwareReset.h"
+#include "Tasks.h"
+
+extern uint32_t _estack; // defined in the linker script
+
+// The following must be kept in line with enum class SoftwareResetReason
+const char *const SoftwareResetData::ReasonText[] =
+{
+ "User",
+ "Erase",
+ "NMI",
+ "Hard fault",
+ "Stuck in spin loop",
+ "Watchdog timeout",
+ "Usage fault",
+ "Other fault",
+ "Stack overflow",
+ "Assertion failed",
+ "Heat task stuck",
+ "Memory protection fault",
+ "Terminate called",
+ "Pure virtual function called",
+ "Deleted virtual function called",
+ "Unknown"
+};
+
+uint8_t SoftwareResetData::extraDebugInfo; // extra info for debugging
+
+// Return true if this struct can be written without erasing it first
+bool SoftwareResetData::IsVacant() const noexcept
+{
+ const uint32_t *p = reinterpret_cast<const uint32_t*>(this);
+ for (size_t i = 0; i < sizeof(*this)/sizeof(uint32_t); ++i)
+ {
+ if (*p != 0xFFFFFFFF)
+ {
+ return false;
+ }
+ ++p;
+ }
+ return true;
+}
+
+void SoftwareResetData::Clear() noexcept
+{
+ memset(this, 0xFF, sizeof(SoftwareResetData));
+}
+
+// Populate this reset data from the parameters passed and the CPU state
+void SoftwareResetData::Populate(uint16_t reason, uint32_t time, const uint32_t *stk) noexcept
+{
+ magic = SoftwareResetData::magicValue;
+ resetReason = reason | ((extraDebugInfo & 0x07) << 5);
+ when = time;
+ neverUsedRam = Tasks::GetNeverUsedRam();
+ hfsr = SCB->HFSR;
+ cfsr = SCB->CFSR;
+ icsr = SCB->ICSR;
+#if USE_MPU
+ if ((reason & (uint16_t)SoftwareResetReason::mainReasonMask) == (uint16_t)SoftwareResetReason::memFault)
+ {
+ bfar = SCB->MMFAR; // on a memory fault we store the MMFAR instead of the BFAR
+ }
+ else
+ {
+ bfar = SCB->BFAR;
+ }
+#else
+ bfar = SCB->BFAR;
+#endif
+ // Get the task name if we can. There may be no task executing, so we must allow for this.
+ const TaskHandle_t currentTask = xTaskGetCurrentTaskHandle();
+ taskName = (currentTask == nullptr) ? 0 : *reinterpret_cast<const uint32_t*>(pcTaskGetName(currentTask));
+
+ if (stk != nullptr)
+ {
+ sp = reinterpret_cast<uint32_t>(stk);
+ for (uint32_t& stval : stack)
+ {
+ stval = (stk < &_estack) ? *stk : 0xFFFFFFFF;
+ ++stk;
+ }
+ }
+}
+
+// End
diff --git a/src/Hardware/SoftwareReset.h b/src/Hardware/SoftwareReset.h
new file mode 100644
index 00000000..e95cc2f8
--- /dev/null
+++ b/src/Hardware/SoftwareReset.h
@@ -0,0 +1,85 @@
+/*
+ * SoftwareReset.h
+ *
+ * Created on: 15 Nov 2019
+ * Author: David
+ */
+
+#ifndef SRC_SOFTWARERESET_H_
+#define SRC_SOFTWARERESET_H_
+
+#include "RepRapFirmware.h"
+
+#if SAM3XA
+# include <DueFlashStorage.h>
+#endif
+
+// Enumeration describing the reasons for a software reset.
+// The spin state gets or'ed into this, so keep the lower 5 bits unused.
+// IMPORTANT! When changing this, also update table SoftwareResetReasonText
+enum class SoftwareResetReason : uint16_t
+{
+ user = 0u, // M999 command
+ erase = 1u << 5, // special M999 command to erase firmware and reset
+ NMI = 2u << 5,
+ hardFault = 3u << 5, // most exceptions get escalated to a hard fault
+ stuckInSpin = 4u << 5, // we got stuck in a Spin() function in the Main task for too long
+ wdtFault = 5u << 5, // secondary watchdog
+ usageFault = 6u << 5,
+ otherFault = 7u << 5,
+ stackOverflow = 8u << 5, // FreeRTOS detected stack overflow
+ assertCalled = 9u << 5, // FreeRTOS assertion failure
+ heaterWatchdog = 10u << 5, // the Heat task didn't kick the watchdog often enough
+ memFault = 11u << 5, // the MPU raised a fault
+ terminateCalled = 12u << 5, // std::terminate was called
+ pureVirtual = 13u << 5,
+ deletedVirtual = 14u << 5,
+
+ mainReasonMask = 0x0F << 5, // mask to pick out the main reason in a uint16_t
+
+ // Bits that are or'ed in
+ unusedBit = 0x0200, // spare bit
+ unused2 = 0x0400, // spare bit
+ inAuxOutput = 0x0800, // this bit is or'ed in if we were in aux output at the time
+ unused_was_inLwipSpin = 0x2000, // no longer used
+ inUsbOutput = 0x4000, // this bit is or'ed in if we were in USB output at the time
+ deliberate = 0x8000 // this but it or'ed in if we deliberately caused a fault
+};
+
+// These are the structures used to hold our non-volatile data.
+// The SAM3X and SAM4E don't have EEPROM so we save the data to flash. This unfortunately means that it gets cleared
+// every time we reprogram the firmware via bossa, but it can be retained when firmware updates are performed
+// via the web interface. That's why it's a good idea to implement versioning here - increase these values
+// whenever the fields of the following structs have changed.
+//
+// The SAM4E has a large page erase size (8K). For this reason we store the software reset data in the 512-byte user signature area
+// instead, which doesn't get cleared when the Erase button is pressed. The SoftareResetData struct must have at least one 32-bit
+// field to guarantee that values of this type will be 32-bit aligned. It must have no virtual members because it is read/written
+// directly from/to flash memory.
+struct SoftwareResetData
+{
+ uint16_t magic; // the magic number, including the version
+ uint16_t resetReason; // this records why we did a software reset, for diagnostic purposes
+ uint32_t neverUsedRam; // the amount of never used RAM at the last abnormal software reset
+ uint32_t hfsr; // hard fault status register
+ uint32_t cfsr; // configurable fault status register
+ uint32_t icsr; // interrupt control and state register
+ uint32_t bfar; // bus fault address register
+ uint32_t sp; // stack pointer
+ uint32_t when; // value of the RTC when the software reset occurred
+ uint32_t taskName; // first 4 bytes of the task name
+ uint32_t stack[29]; // stack when the exception occurred, with the link register and program counter at the bottom
+
+ bool IsVacant() const noexcept; // return true if this struct can be written without erasing it first
+ bool IsValid() const noexcept { return magic == magicValue; }
+ void Clear() noexcept;
+ void Populate(uint16_t reason, uint32_t time, const uint32_t *stk) noexcept;
+
+ static const uint16_t versionValue = 9; // increment this whenever this struct changes
+ static const uint16_t magicValue = 0x7D00 | versionValue; // value we use to recognise that all the flash data has been written
+
+ static const char *const ReasonText[];
+ static uint8_t extraDebugInfo; // extra info for debugging can be stored here
+};
+
+#endif /* SRC_SOFTWARERESET_H_ */