Version 1.17d+1

Save several words of stsck after a hard fault and print them in the
M122 software reset report
Increased the maximum step frequency before the firmware switches to
double stepping
Added diagnostic test for timing the square root calculation
Minor firmware optimisations
This commit is contained in:
David Crocker 2017-02-08 10:45:24 +00:00
parent f35c8cb759
commit 8f6d8e9da7
10 changed files with 111 additions and 86 deletions

View file

@ -534,4 +534,5 @@
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
</scannerConfigBuildInfo>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
</cproject>

View file

@ -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)

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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());

View file

@ -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;
}
};

View file

@ -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

View file

@ -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"

View file

@ -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 */
case FTP_PORT: /* FTP */
interpreter = ftpInterpreter;
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;
}
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;
}
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