diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Libraries/Math/Isqrt.cpp | 7 | ||||
-rw-r--r-- | src/Movement/DDA.cpp | 6 | ||||
-rw-r--r-- | src/Movement/DDA.h | 13 | ||||
-rw-r--r-- | src/Movement/DriveMovement.cpp | 52 | ||||
-rw-r--r-- | src/Platform.cpp | 49 | ||||
-rw-r--r-- | src/Platform.h | 23 | ||||
-rw-r--r-- | src/RepRapFirmware.cpp | 2 | ||||
-rw-r--r-- | src/Version.h | 4 | ||||
-rw-r--r-- | src/Webserver/Webserver.cpp | 40 |
9 files changed, 110 insertions, 86 deletions
diff --git a/src/Libraries/Math/Isqrt.cpp b/src/Libraries/Math/Isqrt.cpp index 4feb0d96..3c8bb990 100644 --- a/src/Libraries/Math/Isqrt.cpp +++ b/src/Libraries/Math/Isqrt.cpp @@ -1,8 +1,5 @@ #include "RepRapFirmware.h" -// The remaining functions are speed-critical, so use full optimisation -#pragma GCC optimize ("O3") - // Fast 62-bit integer square root function (thanks dmould) uint32_t isqrt64(uint64_t num) { @@ -58,7 +55,7 @@ uint32_t isqrt64(uint64_t num) iter64a(14) iter64a(12) iter64a(10) iter64a(8) iter64a(6) iter64a(4) iter64a(2) iter64a(0) - // resHigh is twice the square root of the msw, in the range 0..2^17-1 + // resHigh is twice the square root of the msw, in the range 0..2^16-1 with the input restricted to 62 bits uint64_t numAll = ((uint64_t)numHigh << 32) | (uint32_t)num; #define iter64b(N) \ @@ -74,7 +71,7 @@ uint32_t isqrt64(uint64_t num) // We need to do 16 iterations. // After the last iteration, numAll may be between 0 and (1 + 2 * res) inclusive. - // So to take square roots of numbers up to 64 bits, we need to do all these iterations using 64 bit maths. + // So to take square roots of numbers up to 62 bits, we need to do all these iterations using 64 bit maths. // If we restricted the input to e.g. 48 bits, then we could do some of the final iterations using 32-bit maths. iter64b(30) iter64b(28) iter64b(26) iter64b(24) iter64b(22) iter64b(20) iter64b(18) iter64b(16) diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp index 74d614c2..9afd2b1e 100644 --- a/src/Movement/DDA.cpp +++ b/src/Movement/DDA.cpp @@ -838,9 +838,9 @@ void DDA::Prepare() dm.nextStepTime = 0; dm.stepInterval = 999999; // initialise to a large value so that we will calculate the time for just one step dm.stepsTillRecalc = 0; // so that we don't skip the calculation - bool stepsToDo = (isDeltaMovement && drive < numAxes) - ? dm.CalcNextStepTimeDelta(*this, false) - : dm.CalcNextStepTimeCartesian(*this, false); + const bool stepsToDo = (isDeltaMovement && drive < numAxes) + ? dm.CalcNextStepTimeDelta(*this, false) + : dm.CalcNextStepTimeCartesian(*this, false); if (stepsToDo) { InsertDM(&dm); diff --git a/src/Movement/DDA.h b/src/Movement/DDA.h index 1756c181..4ba65436 100644 --- a/src/Movement/DDA.h +++ b/src/Movement/DDA.h @@ -68,21 +68,22 @@ public: void DebugPrint() const; - static const uint32_t stepClockRate = VARIANT_MCK/32; // the frequency of the clock used for stepper pulse timing, about 0.38us resolution on the Duet + static const uint32_t stepClockRate = VARIANT_MCK/128; // the frequency of the clock used for stepper pulse timing (see Platform::InitialiseInterrupts) static const uint64_t stepClockRateSquared = (uint64_t)stepClockRate * stepClockRate; // Note on the following constant: // If we calculate the step interval on every clock, we reach a point where the calculation time exceeds the step interval. // The worst case is pure Z movement on a delta. On a Mini Kossel with 80 steps/mm with this firmware running on a Duet (84MHx SAM3X8 processor), // the calculation can just be managed in time at speeds of 15000mm/min (step interval 50us), but not at 20000mm/min (step interval 37.5us). - // Therefore, where the step interval falls below 70us, we don't calculate on every step. + // Therefore, where the step interval falls below 60us, we don't calculate on every step. + // Note: the above measurements were taken some time ago, before some firmware optimisations. #ifdef DUET_NG - static const int32_t MinCalcIntervalDelta = (50 * stepClockRate)/1000000; // the smallest sensible interval between calculations (70us) in step timer clocks - static const int32_t MinCalcIntervalCartesian = (50 * stepClockRate)/1000000; // same as delta for now, but could be lower + static const int32_t MinCalcIntervalDelta = (40 * stepClockRate)/1000000; // the smallest sensible interval between calculations (40us) in step timer clocks + static const int32_t MinCalcIntervalCartesian = (40 * stepClockRate)/1000000; // same as delta for now, but could be lower static const uint32_t minInterruptInterval = 6; // about 2us minimum interval between interrupts, in clocks #else - static const int32_t MinCalcIntervalDelta = (70 * stepClockRate)/1000000; // the smallest sensible interval between calculations (70us) in step timer clocks - static const int32_t MinCalcIntervalCartesian = (70 * stepClockRate)/1000000; // same as delta for now, but could be lower + static const int32_t MinCalcIntervalDelta = (60 * stepClockRate)/1000000; // the smallest sensible interval between calculations (60us) in step timer clocks + static const int32_t MinCalcIntervalCartesian = (60 * stepClockRate)/1000000; // same as delta for now, but could be lower static const uint32_t minInterruptInterval = 6; // about 2us minimum interval between interrupts, in clocks #endif diff --git a/src/Movement/DriveMovement.cpp b/src/Movement/DriveMovement.cpp index 1b052f2b..eebd6524 100644 --- a/src/Movement/DriveMovement.cpp +++ b/src/Movement/DriveMovement.cpp @@ -199,7 +199,7 @@ bool DriveMovement::CalcNextStepTimeCartesianFull(const DDA &dda, bool live) pre(nextStep < totalSteps; stepsTillRecalc == 0) { // Work out how many steps to calculate at a time. - uint32_t shiftFactor; + uint32_t shiftFactor = 0; // assume single stepping if (stepInterval < DDA::MinCalcIntervalCartesian) { uint32_t stepsToLimit = ((nextStep <= reverseStartStep && reverseStartStep <= totalSteps) @@ -218,19 +218,12 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0) { shiftFactor = 1; // double stepping } - else - { - shiftFactor = 0; // single stepping - } - } - else - { - shiftFactor = 0; // single stepping } + stepsTillRecalc = (1u << shiftFactor) - 1u; // store number of additional steps to generate - uint32_t nextCalcStep = nextStep + stepsTillRecalc; - uint32_t lastStepTime = nextStepTime; // pick up the time of the last step + const uint32_t nextCalcStep = nextStep + stepsTillRecalc; + const uint32_t lastStepTime = nextStepTime; // pick up the time of the last step if (nextCalcStep < mp.cart.accelStopStep) { // acceleration phase @@ -244,7 +237,7 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0) else if (nextCalcStep < reverseStartStep) { // deceleration phase, not reversed yet - uint64_t temp = mp.cart.twoCsquaredTimesMmPerStepDivA * nextCalcStep; + const uint64_t temp = mp.cart.twoCsquaredTimesMmPerStepDivA * nextCalcStep; // Allow for possible rounding error when the end speed is zero or very small nextStepTime = (twoDistanceToStopTimesCsquaredDivA > temp) ? topSpeedTimesCdivAPlusDecelStartClocks - isqrt64(twoDistanceToStopTimesCsquaredDivA - temp) @@ -273,7 +266,7 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0) // When the end speed is very low, calculating the time of the last step is very sensitive to rounding error. // So if this is the last step and it is late, bring it forward to the expected finish time. // Very rarely on a delta, the penultimate step may also be calculated late. Allow for that here in case it affects Cartesian axes too. - if (nextStep == totalSteps || nextStep + 1 == totalSteps) + if (nextStep + 1 >= totalSteps) { nextStepTime = dda.clocksNeeded; } @@ -295,7 +288,7 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0) { // Work out how many steps to calculate at a time. // The simulator suggests that at 200steps/mm, the minimum step pulse interval for 400mm/sec movement is 4.5us - uint32_t shiftFactor; + uint32_t shiftFactor = 0; // assume single stepping if (stepInterval < DDA::MinCalcIntervalDelta) { const uint32_t stepsToLimit = ((nextStep < reverseStartStep && reverseStartStep <= totalSteps) @@ -318,15 +311,8 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0) { shiftFactor = 1; // double stepping } - else - { - shiftFactor = 0; // single stepping - } - } - else - { - shiftFactor = 0; // single stepping } + stepsTillRecalc = (1u << shiftFactor) - 1; // store number of additional steps to generate if (nextStep == reverseStartStep) @@ -339,19 +325,19 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0) } // Calculate d*s*K as an integer, where d = distance the head has travelled, s = steps/mm for this drive, K = a power of 2 to reduce the rounding errors - if (direction) - { - mp.delta.hmz0sK += (int32_t)(K2 << shiftFactor); - } - else { - mp.delta.hmz0sK -= (int32_t)(K2 << shiftFactor); + int32_t shiftedK2 = (int32_t)(K2 << shiftFactor); + if (!direction) + { + shiftedK2 = -shiftedK2; + } + mp.delta.hmz0sK += shiftedK2; } const int32_t hmz0scK = (int32_t)(((int64_t)mp.delta.hmz0sK * dda.cKc)/Kc); const int32_t t1 = mp.delta.minusAaPlusBbTimesKs + hmz0scK; // Due to rounding error we can end up trying to take the square root of a negative number if we do not take precautions here - const int64_t t2a = (int64_t)isquare64(t1) + mp.delta.dSquaredMinusAsquaredMinusBsquaredTimesKsquaredSsquared - (int64_t)isquare64(mp.delta.hmz0sK); + const int64_t t2a = mp.delta.dSquaredMinusAsquaredMinusBsquaredTimesKsquaredSsquared - (int64_t)isquare64(mp.delta.hmz0sK) + (int64_t)isquare64(t1); const int32_t t2 = (t2a > 0) ? isqrt64(t2a) : 0; const int32_t dsK = (direction) ? t1 - t2 : t1 + t2; @@ -363,7 +349,7 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0) return false; } - uint32_t lastStepTime = nextStepTime; // pick up the time of the last step + const uint32_t lastStepTime = nextStepTime; // pick up the time of the last step if ((uint32_t)dsK < mp.delta.accelStopDsK) { // Acceleration phase @@ -376,7 +362,7 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0) } else { - uint64_t temp = (uint64_t)mp.delta.twoCsquaredTimesMmPerStepDivAK * (uint32_t)dsK; + const uint64_t temp = (uint64_t)mp.delta.twoCsquaredTimesMmPerStepDivAK * (uint32_t)dsK; // Because of possible rounding error when the end speed is zero or very small, we need to check that the square root will work OK nextStepTime = (temp < twoDistanceToStopTimesCsquaredDivA) ? topSpeedTimesCdivAPlusDecelStartClocks - isqrt64(twoDistanceToStopTimesCsquaredDivA - temp) @@ -391,13 +377,13 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0) // When the end speed is very low, calculating the time of the last step is very sensitive to rounding error. // So if this is the last step and it is late, bring it forward to the expected finish time. // Very rarely, the penultimate step may be calculated late, so allow for that too. - if (nextStep == totalSteps || nextStep + 1 == totalSteps) + if (nextStep + 1 >= totalSteps) { nextStepTime = dda.clocksNeeded; } else { - // We don't expect any step except the last to be late + // We don't expect any steps except the last two to be late state = DMState::stepError; stepInterval = 10000000 + nextStepTime; // so we can tell what happened in the debug print return false; diff --git a/src/Platform.cpp b/src/Platform.cpp index 7199ff04..06b3b6b8 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -27,6 +27,7 @@ #include "Network.h" #include "RepRap.h" #include "Webserver.h" +#include "Libraries/Math/Isqrt.h" #include "sam/drivers/tc/tc.h" #include "sam/drivers/hsmci/hsmci.h" @@ -145,8 +146,7 @@ extern "C" // Also get the program counter when the exception occurred. void prvGetRegistersFromStack(const uint32_t *pulFaultStackAddress) { - const uint32_t pc = pulFaultStackAddress[6]; - reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::hardFault, pc); + reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::hardFault, pulFaultStackAddress + 6); } // The fault handler implementation calls a function called prvGetRegistersFromStack() @@ -1219,7 +1219,8 @@ void Platform::Spin() ClassReport(longWait); } -void Platform::SoftwareReset(uint16_t reason, uint32_t pc) +// Perform a software reset. 'stk' points to the program counter on the stack if the cause is an exception, otherwise it is nullptr. +void Platform::SoftwareReset(uint16_t reason, const uint32_t *stk) { wdt_restart(WDT); // kick the watchdog if (reason == (uint16_t)SoftwareResetReason::erase) @@ -1280,7 +1281,14 @@ void Platform::SoftwareReset(uint16_t reason, uint32_t pc) GetStackUsage(NULL, NULL, &srdBuf[slot].neverUsedRam); srdBuf[slot].hfsr = SCB->HFSR; srdBuf[slot].cfsr = SCB->CFSR; - srdBuf[slot].pc = pc; + srdBuf[slot].icsr = SCB->ICSR; + if (stk != nullptr) + { + for (size_t i = 0; i < ARRAY_SIZE(srdBuf[slot].stack); ++i) + { + srdBuf[slot].stack[i] = stk[i]; + } + } // Save diagnostics data to Flash #ifdef DUET_NG @@ -1331,10 +1339,10 @@ void Platform::InitialiseInterrupts() // Timer interrupt for stepper motors // The clock rate we use is a compromise. Too fast and the 64-bit square roots take a long time to execute. Too slow and we lose resolution. - // We choose a clock divisor of 32, which gives us 0.38us resolution. The next option is 128 which would give 1.524us resolution. + // We choose a clock divisor of 128 which gives 1.524us resolution on the Duet 085 (84MHz clock) and 0.9375us resolution on the Duet WiFi. pmc_set_writeprotect(false); pmc_enable_periph_clk((uint32_t) STEP_TC_IRQN); - tc_init(STEP_TC, STEP_TC_CHAN, TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK3); + tc_init(STEP_TC, STEP_TC_CHAN, TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK4); STEP_TC->TC_CHANNEL[STEP_TC_CHAN].TC_IDR = ~(uint32_t)0; // interrupts disabled for now tc_start(STEP_TC, STEP_TC_CHAN); tc_get_status(STEP_TC, STEP_TC_CHAN); // clear any pending interrupt @@ -1445,9 +1453,15 @@ void Platform::Diagnostics(MessageType mtype) Message(mtype, "Last software reset code "); if (slot >= 0 && srdBuf[slot].magic == SoftwareResetData::magicValue) { - MessageF(mtype, "0x%04x, PC 0x%08x, HFSR 0x%08x, CFSR 0x%08x, available RAM %u bytes (slot %d)\n", - srdBuf[slot].resetReason, srdBuf[slot].pc, srdBuf[slot].hfsr, srdBuf[slot].cfsr, srdBuf[slot].neverUsedRam, slot); - MessageF(mtype, "Spinning module during software reset: %s\n", moduleName[srdBuf[slot].resetReason & 0x0F]); + scratchString.Clear(); + for (size_t i = 0; i < ARRAY_SIZE(srdBuf[slot].stack); ++i) + { + scratchString.catf(" %08x", srdBuf[slot].stack[i]); + } + MessageF(mtype, "0x%04x, HFSR 0x%08x, CFSR 0x%08x, ICSR 0x%08x\nStack:%s\n", + srdBuf[slot].resetReason, srdBuf[slot].hfsr, srdBuf[slot].cfsr, srdBuf[slot].icsr, scratchString.Pointer()); + MessageF(mtype, "Spinning module during software reset: %s, available RAM %u bytes (slot %d)\n", + moduleName[srdBuf[slot].resetReason & 0x0F], srdBuf[slot].neverUsedRam, slot); } else { @@ -1583,6 +1597,23 @@ void Platform::DiagnosticTest(int d) DDA::PrintMoves(); break; + case (int)DiagnosticTestType::TimeSquareRoot: // Show the square root calculation time. The displayed value is subject to interrupts. + { + const uint32_t num1 = 0x7265ac3d; + const uint32_t now1 = Platform::GetInterruptClocks(); + const uint32_t num1a = isqrt64((uint64_t)num1 * num1); + const uint32_t tim1 = Platform::GetInterruptClocks() - now1; + + const uint32_t num2 = 0x0000a4c5; + const uint32_t now2 = Platform::GetInterruptClocks(); + const uint32_t num2a = isqrt64((uint64_t)num2 * num2); + const uint32_t tim2 = Platform::GetInterruptClocks() - now2; + MessageF(GENERIC_MESSAGE, "Square roots: 64-bit %.1fus %s, 32-bit %.1fus %s\n", + (float)(tim1 * 1000000)/DDA::stepClockRate, (num1a == num1) ? "ok" : "ERROR", + (float)(tim2 * 1000000)/DDA::stepClockRate, (num2a == num2) ? "ok" : "ERROR"); + } + break; + #ifdef DUET_NG case (int)DiagnosticTestType::PrintExpanderStatus: MessageF(GENERIC_MESSAGE, "Expander status %04X\n", DuetExpansion::DiagnosticRead()); diff --git a/src/Platform.h b/src/Platform.h index f887f907..52f7d453 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -180,6 +180,7 @@ enum class DiagnosticTestType : int #ifdef DUET_NG PrintExpanderStatus = 101, // print DueXn expander status #endif + TimeSquareRoot = 102 // do a timing test on the square roor function }; // Enumeration to describe what we want to do with a logical pin @@ -326,7 +327,7 @@ public: void ClassReport(float &lastTime); // Called on Spin() return to check everything's live. void LogError(ErrorCode e) { errorCodeBits |= (uint32_t)e; } - void SoftwareReset(uint16_t reason, uint32_t pc = 0); + void SoftwareReset(uint16_t reason, const uint32_t *stk = nullptr); bool AtxPower() const; void SetAtxPower(bool on); void SetBoardType(BoardType bt); @@ -609,22 +610,30 @@ private: // directly from/to flash memory. struct SoftwareResetData { - static const uint16_t versionValue = 4; // increment this whenever this struct changes + static const uint16_t versionValue = 6; // 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 uint32_t nvAddress = 0; // must be 4-byte aligned - static const size_t numberOfSlots = 8; // number of storage slots used to implement wear levelling + static const size_t numberOfSlots = 6; // number of storage slots used to implement wear levelling 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 pc; // program counter when the exception occurred + uint32_t icsr; // interrupt control and state register + uint32_t stack[16]; // stack when the exception occurred, with the program counter at the bottom bool isVacant() const // return true if this struct can be written without erasing it first { - return magic == 0xFFFF && resetReason == 0xFFFF && neverUsedRam == 0xFFFFFFFF - && hfsr == 0xFFFFFFFF && cfsr == 0xFFFFFFFF && pc == 0xFFFFFFFF; + 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; } }; diff --git a/src/RepRapFirmware.cpp b/src/RepRapFirmware.cpp index 9a09fbd3..97ea7f37 100644 --- a/src/RepRapFirmware.cpp +++ b/src/RepRapFirmware.cpp @@ -186,7 +186,7 @@ const char *moduleName[] = // Utilities and storage not part of any class -static char scratchStringBuffer[140]; // this is now used only for short messages; needs to be long enough to print delta parameters +static char scratchStringBuffer[150]; // this is now used only for short messages; needs to be long enough to print delta parameters and 12 words of stack (132 bytes) StringRef scratchString(scratchStringBuffer, ARRAY_SIZE(scratchStringBuffer)); // For debug use diff --git a/src/Version.h b/src/Version.h index 51ed7b46..fa8bf2e9 100644 --- a/src/Version.h +++ b/src/Version.h @@ -9,11 +9,11 @@ #define SRC_VERSION_H_ #ifndef VERSION -# define VERSION "1.17d" +# define VERSION "1.17d+1" #endif #ifndef DATE -# define DATE "2017-01-28" +# define DATE "2017-02-01" #endif #define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman" diff --git a/src/Webserver/Webserver.cpp b/src/Webserver/Webserver.cpp index 9c11c4bc..620e6246 100644 --- a/src/Webserver/Webserver.cpp +++ b/src/Webserver/Webserver.cpp @@ -343,32 +343,32 @@ uint16_t Webserver::GetGCodeBufferSpace(const WebSource source) const void Webserver::ConnectionLost(Connection conn) { // Inform protocol handlers that this connection has been lost - uint16_t localPort = Network::GetLocalPort(conn); + const uint16_t localPort = Network::GetLocalPort(conn); ProtocolInterpreter *interpreter; switch (localPort) { - case FTP_PORT: /* FTP */ - interpreter = ftpInterpreter; - break; + case FTP_PORT: /* FTP */ + interpreter = ftpInterpreter; + break; - case TELNET_PORT: /* Telnet */ - interpreter = telnetInterpreter; - break; + case TELNET_PORT: /* Telnet */ + interpreter = telnetInterpreter; + break; - default: /* HTTP and FTP data */ - if (localPort == network->GetHttpPort()) - { - interpreter = httpInterpreter; - break; - } - else if (localPort == network->GetDataPort()) - { - interpreter = ftpInterpreter; - break; - } + default: /* HTTP and FTP data */ + if (localPort == network->GetHttpPort()) + { + interpreter = httpInterpreter; + break; + } + else if (localPort == network->GetDataPort()) + { + interpreter = ftpInterpreter; + break; + } - platform->MessageF(GENERIC_MESSAGE, "Error: Webserver should handle disconnect event at local port %d, but no handler was found!\n", localPort); - return; + platform->MessageF(GENERIC_MESSAGE, "Error: Webserver should handle disconnect event at local port %d, but no handler was found!\n", localPort); + return; } // Print some debug information and notify the protocol interpreter |