update code base to Marlin 2.0.9.2
This commit is contained in:
176
Marlin/src/HAL/shared/Delay.cpp
Normal file
176
Marlin/src/HAL/shared/Delay.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Delay.h"
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if defined(__arm__) || defined(__thumb__)
|
||||
|
||||
static uint32_t ASM_CYCLES_PER_ITERATION = 4; // Initial bet which will be adjusted in calibrate_delay_loop
|
||||
|
||||
// Simple assembler loop counting down
|
||||
void delay_asm(uint32_t cy) {
|
||||
cy = _MAX(cy / ASM_CYCLES_PER_ITERATION, 1U); // Zero is forbidden here
|
||||
__asm__ __volatile__(
|
||||
A(".syntax unified") // is to prevent CM0,CM1 non-unified syntax
|
||||
L("1")
|
||||
A("subs %[cnt],#1")
|
||||
A("bne 1b")
|
||||
: [cnt]"+r"(cy) // output: +r means input+output
|
||||
: // input:
|
||||
: "cc" // clobbers:
|
||||
);
|
||||
}
|
||||
|
||||
// We can't use CMSIS since it's not available on all platform, so fallback to hardcoded register values
|
||||
#define HW_REG(X) *(volatile uint32_t *)(X)
|
||||
#define _DWT_CTRL 0xE0001000
|
||||
#define _DWT_CYCCNT 0xE0001004 // CYCCNT is 32bits, takes 37s or so to wrap.
|
||||
#define _DEM_CR 0xE000EDFC
|
||||
#define _LAR 0xE0001FB0
|
||||
|
||||
// Use hardware cycle counter instead, it's much safer
|
||||
void delay_dwt(uint32_t count) {
|
||||
// Reuse the ASM_CYCLES_PER_ITERATION variable to avoid wasting another useless variable
|
||||
uint32_t start = HW_REG(_DWT_CYCCNT) - ASM_CYCLES_PER_ITERATION, elapsed;
|
||||
do {
|
||||
elapsed = HW_REG(_DWT_CYCCNT) - start;
|
||||
} while (elapsed < count);
|
||||
}
|
||||
|
||||
// Pointer to asm function, calling the functions has a ~20 cycles overhead
|
||||
DelayImpl DelayCycleFnc = delay_asm;
|
||||
|
||||
void calibrate_delay_loop() {
|
||||
// Check if we have a working DWT implementation in the CPU (see https://developer.arm.com/documentation/ddi0439/b/Data-Watchpoint-and-Trace-Unit/DWT-Programmers-Model)
|
||||
if (!HW_REG(_DWT_CTRL)) {
|
||||
// No DWT present, so fallback to plain old ASM nop counting
|
||||
// Unfortunately, we don't exactly know how many iteration it'll take to decrement a counter in a loop
|
||||
// It depends on the CPU architecture, the code current position (flash vs SRAM)
|
||||
// So, instead of wild guessing and making mistake, instead
|
||||
// compute it once for all
|
||||
ASM_CYCLES_PER_ITERATION = 1;
|
||||
// We need to fetch some reference clock before waiting
|
||||
cli();
|
||||
uint32_t start = micros();
|
||||
delay_asm(1000); // On a typical CPU running in MHz, waiting 1000 "unknown cycles" means it'll take between 1ms to 6ms, that's perfectly acceptable
|
||||
uint32_t end = micros();
|
||||
sei();
|
||||
uint32_t expectedCycles = (end - start) * ((F_CPU) / 1000000UL); // Convert microseconds to cycles
|
||||
// Finally compute the right scale
|
||||
ASM_CYCLES_PER_ITERATION = (uint32_t)(expectedCycles / 1000);
|
||||
|
||||
// No DWT present, likely a Cortex M0 so NOP counting is our best bet here
|
||||
DelayCycleFnc = delay_asm;
|
||||
}
|
||||
else {
|
||||
// Enable DWT counter
|
||||
// From https://stackoverflow.com/a/41188674/1469714
|
||||
HW_REG(_DEM_CR) = HW_REG(_DEM_CR) | 0x01000000; // Enable trace
|
||||
#if __CORTEX_M == 7
|
||||
HW_REG(_LAR) = 0xC5ACCE55; // Unlock access to DWT registers, see https://developer.arm.com/documentation/ihi0029/e/ section B2.3.10
|
||||
#endif
|
||||
HW_REG(_DWT_CYCCNT) = 0; // Clear DWT cycle counter
|
||||
HW_REG(_DWT_CTRL) = HW_REG(_DWT_CTRL) | 1; // Enable DWT cycle counter
|
||||
|
||||
// Then calibrate the constant offset from the counter
|
||||
ASM_CYCLES_PER_ITERATION = 0;
|
||||
uint32_t s = HW_REG(_DWT_CYCCNT);
|
||||
uint32_t e = HW_REG(_DWT_CYCCNT); // (e - s) contains the number of cycle required to read the cycle counter
|
||||
delay_dwt(0);
|
||||
uint32_t f = HW_REG(_DWT_CYCCNT); // (f - e) contains the delay to call the delay function + the time to read the cycle counter
|
||||
ASM_CYCLES_PER_ITERATION = (f - e) - (e - s);
|
||||
|
||||
// Use safer DWT function
|
||||
DelayCycleFnc = delay_dwt;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLED(MARLIN_DEV_MODE)
|
||||
void dump_delay_accuracy_check() {
|
||||
auto report_call_time = [](PGM_P const name, PGM_P const unit, const uint32_t cycles, const uint32_t total, const bool do_flush=true) {
|
||||
SERIAL_ECHOPGM("Calling ");
|
||||
SERIAL_ECHOPGM_P(name);
|
||||
SERIAL_ECHOLNPGM(" for ", cycles);
|
||||
SERIAL_ECHOPGM_P(unit);
|
||||
SERIAL_ECHOLNPGM(" took: ", total);
|
||||
SERIAL_ECHOPGM_P(unit);
|
||||
if (do_flush) SERIAL_FLUSHTX();
|
||||
};
|
||||
|
||||
uint32_t s, e;
|
||||
|
||||
SERIAL_ECHOLNPGM("Computed delay calibration value: ", ASM_CYCLES_PER_ITERATION);
|
||||
SERIAL_FLUSH();
|
||||
// Display the results of the calibration above
|
||||
constexpr uint32_t testValues[] = { 1, 5, 10, 20, 50, 100, 150, 200, 350, 500, 750, 1000 };
|
||||
for (auto i : testValues) {
|
||||
s = micros(); DELAY_US(i); e = micros();
|
||||
report_call_time(PSTR("delay"), PSTR("us"), i, e - s);
|
||||
}
|
||||
|
||||
if (HW_REG(_DWT_CTRL)) {
|
||||
for (auto i : testValues) {
|
||||
s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(i); e = HW_REG(_DWT_CYCCNT);
|
||||
report_call_time(PSTR("runtime delay"), PSTR("cycles"), i, e - s);
|
||||
}
|
||||
|
||||
// Measure the delay to call a real function compared to a function pointer
|
||||
s = HW_REG(_DWT_CYCCNT); delay_dwt(1); e = HW_REG(_DWT_CYCCNT);
|
||||
report_call_time(PSTR("delay_dwt"), PSTR("cycles"), 1, e - s);
|
||||
|
||||
static PGMSTR(dcd, "DELAY_CYCLES directly ");
|
||||
|
||||
s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES( 1); e = HW_REG(_DWT_CYCCNT);
|
||||
report_call_time(dcd, PSTR("cycles"), 1, e - s, false);
|
||||
|
||||
s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES( 5); e = HW_REG(_DWT_CYCCNT);
|
||||
report_call_time(dcd, PSTR("cycles"), 5, e - s, false);
|
||||
|
||||
s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(10); e = HW_REG(_DWT_CYCCNT);
|
||||
report_call_time(dcd, PSTR("cycles"), 10, e - s, false);
|
||||
|
||||
s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(20); e = HW_REG(_DWT_CYCCNT);
|
||||
report_call_time(dcd, PSTR("cycles"), 20, e - s, false);
|
||||
|
||||
s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(50); e = HW_REG(_DWT_CYCCNT);
|
||||
report_call_time(dcd, PSTR("cycles"), 50, e - s, false);
|
||||
|
||||
s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(100); e = HW_REG(_DWT_CYCCNT);
|
||||
report_call_time(dcd, PSTR("cycles"), 100, e - s, false);
|
||||
|
||||
s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(200); e = HW_REG(_DWT_CYCCNT);
|
||||
report_call_time(dcd, PSTR("cycles"), 200, e - s, false);
|
||||
}
|
||||
}
|
||||
#endif // MARLIN_DEV_MODE
|
||||
|
||||
|
||||
#else
|
||||
|
||||
void calibrate_delay_loop() {}
|
||||
#if ENABLED(MARLIN_DEV_MODE)
|
||||
void dump_delay_accuracy_check() { SERIAL_ECHOPGM_P(PSTR("N/A on this platform")); }
|
||||
#endif
|
||||
|
||||
#endif
|
249
Marlin/src/HAL/shared/Delay.h
Executable file → Normal file
249
Marlin/src/HAL/shared/Delay.h
Executable file → Normal file
@@ -16,11 +16,13 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
/**
|
||||
* Busy wait delay cycles routines:
|
||||
*
|
||||
@@ -31,131 +33,176 @@
|
||||
|
||||
#include "../../core/macros.h"
|
||||
|
||||
void calibrate_delay_loop();
|
||||
|
||||
#if defined(__arm__) || defined(__thumb__)
|
||||
|
||||
#if __CORTEX_M == 7
|
||||
// We want to have delay_cycle function with the lowest possible overhead, so we adjust at the function at runtime based on the current CPU best feature
|
||||
typedef void (*DelayImpl)(uint32_t);
|
||||
extern DelayImpl DelayCycleFnc;
|
||||
|
||||
// Cortex-M3 through M7 can use the cycle counter of the DWT unit
|
||||
// http://www.anthonyvh.com/2017/05/18/cortex_m-cycle_counter/
|
||||
// I've measured 36 cycles on my system to call the cycle waiting method, but it shouldn't change much to have a bit more margin, it only consume a bit more flash
|
||||
#define TRIP_POINT_FOR_CALLING_FUNCTION 40
|
||||
|
||||
FORCE_INLINE static void enableCycleCounter() {
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
|
||||
#if __CORTEX_M == 7
|
||||
DWT->LAR = 0xC5ACCE55; // Unlock DWT on the M7
|
||||
#endif
|
||||
|
||||
DWT->CYCCNT = 0;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
// A simple recursive template class that output exactly one 'nop' of code per recursion
|
||||
template <int N> struct NopWriter {
|
||||
FORCE_INLINE static void build() {
|
||||
__asm__ __volatile__("nop");
|
||||
NopWriter<N-1>::build();
|
||||
}
|
||||
};
|
||||
// End the loop
|
||||
template <> struct NopWriter<0> { FORCE_INLINE static void build() {} };
|
||||
|
||||
FORCE_INLINE volatile uint32_t getCycleCount() { return DWT->CYCCNT; }
|
||||
|
||||
FORCE_INLINE static void DELAY_CYCLES(const uint32_t x) {
|
||||
const uint32_t endCycles = getCycleCount() + x;
|
||||
while (PENDING(getCycleCount(), endCycles)) {}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// https://blueprints.launchpad.net/gcc-arm-embedded/+spec/delay-cycles
|
||||
|
||||
#define nop() __asm__ __volatile__("nop;\n\t":::)
|
||||
|
||||
FORCE_INLINE static void __delay_4cycles(uint32_t cy) { // +1 cycle
|
||||
#if ARCH_PIPELINE_RELOAD_CYCLES < 2
|
||||
#define EXTRA_NOP_CYCLES A("nop")
|
||||
#else
|
||||
#define EXTRA_NOP_CYCLES ""
|
||||
#endif
|
||||
|
||||
__asm__ __volatile__(
|
||||
A(".syntax unified") // is to prevent CM0,CM1 non-unified syntax
|
||||
L("1")
|
||||
A("subs %[cnt],#1")
|
||||
EXTRA_NOP_CYCLES
|
||||
A("bne 1b")
|
||||
: [cnt]"+r"(cy) // output: +r means input+output
|
||||
: // input:
|
||||
: "cc" // clobbers:
|
||||
);
|
||||
}
|
||||
|
||||
// Delay in cycles
|
||||
FORCE_INLINE static void DELAY_CYCLES(uint32_t x) {
|
||||
|
||||
if (__builtin_constant_p(x)) {
|
||||
#define MAXNOPS 4
|
||||
|
||||
if (x <= (MAXNOPS)) {
|
||||
switch (x) { case 4: nop(); case 3: nop(); case 2: nop(); case 1: nop(); }
|
||||
}
|
||||
else { // because of +1 cycle inside delay_4cycles
|
||||
const uint32_t rem = (x - 1) % (MAXNOPS);
|
||||
switch (rem) { case 3: nop(); case 2: nop(); case 1: nop(); }
|
||||
if ((x = (x - 1) / (MAXNOPS)))
|
||||
__delay_4cycles(x); // if need more then 4 nop loop is more optimal
|
||||
}
|
||||
#undef MAXNOPS
|
||||
namespace Private {
|
||||
// Split recursing template in 2 different class so we don't reach the maximum template instantiation depth limit
|
||||
template <bool belowTP, int N> struct Helper {
|
||||
FORCE_INLINE static void build() {
|
||||
DelayCycleFnc(N - 2); // Approximative cost of calling the function (might be off by one or 2 cycles)
|
||||
}
|
||||
else if ((x >>= 2))
|
||||
__delay_4cycles(x);
|
||||
}
|
||||
#undef nop
|
||||
};
|
||||
|
||||
#endif
|
||||
template <int N> struct Helper<true, N> {
|
||||
FORCE_INLINE static void build() {
|
||||
NopWriter<N - 1>::build();
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct Helper<true, 0> {
|
||||
FORCE_INLINE static void build() {}
|
||||
};
|
||||
|
||||
}
|
||||
// Select a behavior based on the constexpr'ness of the parameter
|
||||
// If called with a compile-time parameter, then write as many NOP as required to reach the asked cycle count
|
||||
// (there is some tripping point here to start looping when it's more profitable than gruntly executing NOPs)
|
||||
// If not called from a compile-time parameter, fallback to a runtime loop counting version instead
|
||||
template <bool compileTime, int Cycles>
|
||||
struct SmartDelay {
|
||||
FORCE_INLINE SmartDelay(int) {
|
||||
if (Cycles == 0) return;
|
||||
Private::Helper<Cycles < TRIP_POINT_FOR_CALLING_FUNCTION, Cycles>::build();
|
||||
}
|
||||
};
|
||||
// Runtime version below. There is no way this would run under less than ~TRIP_POINT_FOR_CALLING_FUNCTION cycles
|
||||
template <int T>
|
||||
struct SmartDelay<false, T> {
|
||||
FORCE_INLINE SmartDelay(int v) { DelayCycleFnc(v); }
|
||||
};
|
||||
|
||||
#define DELAY_CYCLES(X) do { SmartDelay<IS_CONSTEXPR(X), IS_CONSTEXPR(X) ? X : 0> _smrtdly_X(X); } while(0)
|
||||
|
||||
// For delay in microseconds, no smart delay selection is required, directly call the delay function
|
||||
// Teensy compiler is too old and does not accept smart delay compile-time / run-time selection correctly
|
||||
#define DELAY_US(x) DelayCycleFnc((x) * ((F_CPU) / 1000000UL))
|
||||
|
||||
#elif defined(__AVR__)
|
||||
|
||||
#define nop() __asm__ __volatile__("nop;\n\t":::)
|
||||
|
||||
FORCE_INLINE static void __delay_4cycles(uint8_t cy) {
|
||||
__asm__ __volatile__(
|
||||
L("1")
|
||||
A("dec %[cnt]")
|
||||
A("nop")
|
||||
A("brne 1b")
|
||||
: [cnt] "+r"(cy) // output: +r means input+output
|
||||
: // input:
|
||||
: "cc" // clobbers:
|
||||
);
|
||||
FORCE_INLINE static void __delay_up_to_3c(uint8_t cycles) {
|
||||
switch (cycles) {
|
||||
case 3:
|
||||
__asm__ __volatile__(A("RJMP .+0") A("NOP"));
|
||||
break;
|
||||
case 2:
|
||||
__asm__ __volatile__(A("RJMP .+0"));
|
||||
break;
|
||||
case 1:
|
||||
__asm__ __volatile__(A("NOP"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Delay in cycles
|
||||
FORCE_INLINE static void DELAY_CYCLES(uint16_t x) {
|
||||
|
||||
if (__builtin_constant_p(x)) {
|
||||
#define MAXNOPS 4
|
||||
|
||||
if (x <= (MAXNOPS)) {
|
||||
switch (x) { case 4: nop(); case 3: nop(); case 2: nop(); case 1: nop(); }
|
||||
FORCE_INLINE static void DELAY_CYCLES(uint16_t cycles) {
|
||||
if (__builtin_constant_p(cycles)) {
|
||||
if (cycles <= 3) {
|
||||
__delay_up_to_3c(cycles);
|
||||
}
|
||||
else if (cycles == 4) {
|
||||
__delay_up_to_3c(2);
|
||||
__delay_up_to_3c(2);
|
||||
}
|
||||
else {
|
||||
const uint32_t rem = (x) % (MAXNOPS);
|
||||
switch (rem) { case 3: nop(); case 2: nop(); case 1: nop(); }
|
||||
if ((x = (x) / (MAXNOPS)))
|
||||
__delay_4cycles(x); // if need more then 4 nop loop is more optimal
|
||||
cycles -= 1 + 4; // Compensate for the first LDI (1) and the first round (4)
|
||||
__delay_up_to_3c(cycles % 4);
|
||||
|
||||
cycles /= 4;
|
||||
// The following code burns [1 + 4 * (rounds+1)] cycles
|
||||
uint16_t dummy;
|
||||
__asm__ __volatile__(
|
||||
// "manually" load counter from constants, otherwise the compiler may optimize this part away
|
||||
A("LDI %A[rounds], %[l]") // 1c
|
||||
A("LDI %B[rounds], %[h]") // 1c (compensating the non branching BRCC)
|
||||
L("1")
|
||||
A("SBIW %[rounds], 1") // 2c
|
||||
A("BRCC 1b") // 2c when branching, else 1c (end of loop)
|
||||
: // Outputs ...
|
||||
[rounds] "=w" (dummy) // Restrict to a wo (=) 16 bit register pair (w)
|
||||
: // Inputs ...
|
||||
[l] "M" (cycles%256), // Restrict to 0..255 constant (M)
|
||||
[h] "M" (cycles/256) // Restrict to 0..255 constant (M)
|
||||
:// Clobbers ...
|
||||
"cc" // Indicate we are modifying flags like Carry (cc)
|
||||
);
|
||||
}
|
||||
|
||||
#undef MAXNOPS
|
||||
}
|
||||
else if ((x >>= 2))
|
||||
__delay_4cycles(x);
|
||||
else {
|
||||
__asm__ __volatile__(
|
||||
L("1")
|
||||
A("SBIW %[cycles], 4") // 2c
|
||||
A("BRCC 1b") // 2c when branching, else 1c (end of loop)
|
||||
: [cycles] "+w" (cycles) // output: Restrict to a rw (+) 16 bit register pair (w)
|
||||
: // input: -
|
||||
: "cc" // clobbers: We are modifying flags like Carry (cc)
|
||||
);
|
||||
}
|
||||
}
|
||||
#undef nop
|
||||
|
||||
#elif defined(__PLAT_LINUX__) || defined(ESP32)
|
||||
// Delay in microseconds
|
||||
#define DELAY_US(x) DELAY_CYCLES((x) * ((F_CPU) / 1000000UL))
|
||||
|
||||
// specified inside platform
|
||||
#elif defined(ESP32) || defined(__PLAT_LINUX__) || defined(__PLAT_NATIVE_SIM__)
|
||||
|
||||
// DELAY_CYCLES specified inside platform
|
||||
|
||||
// Delay in microseconds
|
||||
#define DELAY_US(x) DELAY_CYCLES((x) * ((F_CPU) / 1000000UL))
|
||||
#else
|
||||
|
||||
#error "Unsupported MCU architecture"
|
||||
|
||||
#endif
|
||||
|
||||
// Delay in nanoseconds
|
||||
#define DELAY_NS(x) DELAY_CYCLES( (x) * (F_CPU / 1000000UL) / 1000UL )
|
||||
/**************************************************************
|
||||
* Delay in nanoseconds. Requires the F_CPU macro.
|
||||
* These macros follow avr-libc delay conventions.
|
||||
*
|
||||
* For AVR there are three possible operation modes, due to its
|
||||
* slower clock speeds and thus coarser delay resolution. For
|
||||
* example, when F_CPU = 16000000 the resolution is 62.5ns.
|
||||
*
|
||||
* Round up (default)
|
||||
* Round up the delay according to the CPU clock resolution.
|
||||
* e.g., 100 will give a delay of 2 cycles (125ns).
|
||||
*
|
||||
* Round down (DELAY_NS_ROUND_DOWN)
|
||||
* Round down the delay according to the CPU clock resolution.
|
||||
* e.g., 100 will be rounded down to 1 cycle (62.5ns).
|
||||
*
|
||||
* Nearest (DELAY_NS_ROUND_CLOSEST)
|
||||
* Round the delay to the nearest number of clock cycles.
|
||||
* e.g., 165 will be rounded up to 3 cycles (187.5ns) because
|
||||
* it's closer to the requested delay than 2 cycle (125ns).
|
||||
*/
|
||||
|
||||
// Delay in microseconds
|
||||
#define DELAY_US(x) DELAY_CYCLES( (x) * (F_CPU / 1000000UL) )
|
||||
#ifndef __AVR__
|
||||
#undef DELAY_NS_ROUND_DOWN
|
||||
#undef DELAY_NS_ROUND_CLOSEST
|
||||
#endif
|
||||
|
||||
#if ENABLED(DELAY_NS_ROUND_DOWN)
|
||||
#define DELAY_NS(x) DELAY_CYCLES((x) * ((F_CPU) / 1000000UL) / 1000UL) // floor
|
||||
#elif ENABLED(DELAY_NS_ROUND_CLOSEST)
|
||||
#define DELAY_NS(x) DELAY_CYCLES(((x) * ((F_CPU) / 1000000UL) + 500) / 1000UL) // round
|
||||
#else
|
||||
#define DELAY_NS(x) DELAY_CYCLES(((x) * ((F_CPU) / 1000000UL) + 999) / 1000UL) // "ceil"
|
||||
#endif
|
||||
|
33
Marlin/src/HAL/shared/HAL_MinSerial.cpp
Normal file
33
Marlin/src/HAL/shared/HAL_MinSerial.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "HAL_MinSerial.h"
|
||||
|
||||
#if ENABLED(POSTMORTEM_DEBUGGING)
|
||||
|
||||
void HAL_min_serial_init_default() {}
|
||||
void HAL_min_serial_out_default(char ch) { SERIAL_CHAR(ch); }
|
||||
void (*HAL_min_serial_init)() = &HAL_min_serial_init_default;
|
||||
void (*HAL_min_serial_out)(char) = &HAL_min_serial_out_default;
|
||||
|
||||
bool MinSerial::force_using_default_output = false;
|
||||
|
||||
#endif
|
79
Marlin/src/HAL/shared/HAL_MinSerial.h
Normal file
79
Marlin/src/HAL/shared/HAL_MinSerial.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../../core/serial.h"
|
||||
#include <stdint.h>
|
||||
|
||||
// Serial stuff here
|
||||
// Inside an exception handler, the CPU state is not safe, we can't expect the handler to resume
|
||||
// and the software to continue. UART communication can't rely on later callback/interrupt as it might never happen.
|
||||
// So, you need to provide some method to send one byte to the usual UART with the interrupts disabled
|
||||
// By default, the method uses SERIAL_CHAR but it's 100% guaranteed to break (couldn't be worse than nothing...)7
|
||||
extern void (*HAL_min_serial_init)();
|
||||
extern void (*HAL_min_serial_out)(char ch);
|
||||
|
||||
struct MinSerial {
|
||||
static bool force_using_default_output;
|
||||
// Serial output
|
||||
static void TX(char ch) {
|
||||
if (force_using_default_output)
|
||||
SERIAL_CHAR(ch);
|
||||
else
|
||||
HAL_min_serial_out(ch);
|
||||
}
|
||||
// Send String through UART
|
||||
static void TX(const char *s) { while (*s) TX(*s++); }
|
||||
// Send a digit through UART
|
||||
static void TXDigit(uint32_t d) {
|
||||
if (d < 10) TX((char)(d+'0'));
|
||||
else if (d < 16) TX((char)(d+'A'-10));
|
||||
else TX('?');
|
||||
}
|
||||
|
||||
// Send Hex number through UART
|
||||
static void TXHex(uint32_t v) {
|
||||
TX("0x");
|
||||
for (uint8_t i = 0; i < 8; i++, v <<= 4)
|
||||
TXDigit((v >> 28) & 0xF);
|
||||
}
|
||||
|
||||
// Send Decimal number through UART
|
||||
static void TXDec(uint32_t v) {
|
||||
if (!v) {
|
||||
TX('0');
|
||||
return;
|
||||
}
|
||||
|
||||
char nbrs[14];
|
||||
char *p = &nbrs[0];
|
||||
while (v != 0) {
|
||||
*p++ = '0' + (v % 10);
|
||||
v /= 10;
|
||||
}
|
||||
do {
|
||||
p--;
|
||||
TX(*p);
|
||||
} while (p != &nbrs[0]);
|
||||
}
|
||||
static void init() { if (!force_using_default_output) HAL_min_serial_init(); }
|
||||
};
|
8
Marlin/src/HAL/shared/HAL_SPI.h
Executable file → Normal file
8
Marlin/src/HAL/shared/HAL_SPI.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -71,10 +71,10 @@ void spiSend(uint8_t b);
|
||||
uint8_t spiRec();
|
||||
|
||||
// Read from SPI into buffer
|
||||
void spiRead(uint8_t* buf, uint16_t nbyte);
|
||||
void spiRead(uint8_t *buf, uint16_t nbyte);
|
||||
|
||||
// Write token and then write from 512 byte buffer to SPI (for SD card)
|
||||
void spiSendBlock(uint8_t token, const uint8_t* buf);
|
||||
void spiSendBlock(uint8_t token, const uint8_t *buf);
|
||||
|
||||
// Begin SPI transaction, set clock, bit order, data mode
|
||||
void spiBeginTransaction(uint32_t spiClock, uint8_t bitOrder, uint8_t dataMode);
|
||||
@@ -87,7 +87,7 @@ void spiBeginTransaction(uint32_t spiClock, uint8_t bitOrder, uint8_t dataMode);
|
||||
void spiSend(uint32_t chan, byte b);
|
||||
|
||||
// Write buffer to specified SPI channel
|
||||
void spiSend(uint32_t chan, const uint8_t* buf, size_t n);
|
||||
void spiSend(uint32_t chan, const uint8_t *buf, size_t n);
|
||||
|
||||
// Read single byte from specified SPI channel
|
||||
uint8_t spiRec(uint32_t chan);
|
||||
|
4
Marlin/src/HAL/shared/HAL_ST7920.h
Executable file → Normal file
4
Marlin/src/HAL/shared/HAL_ST7920.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -27,7 +27,7 @@
|
||||
* (bypassing U8G), it will allow the LIGHTWEIGHT_UI to operate.
|
||||
*/
|
||||
|
||||
#if HAS_GRAPHICAL_LCD && ENABLED(LIGHTWEIGHT_UI)
|
||||
#if BOTH(HAS_MARLINUI_U8GLIB, LIGHTWEIGHT_UI)
|
||||
void ST7920_cs();
|
||||
void ST7920_ncs();
|
||||
void ST7920_set_cmd();
|
||||
|
2
Marlin/src/HAL/shared/HAL_spi_L6470.cpp
Executable file → Normal file
2
Marlin/src/HAL/shared/HAL_spi_L6470.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
24
Marlin/src/HAL/shared/Marduino.h
Executable file → Normal file
24
Marlin/src/HAL/shared/Marduino.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -28,9 +28,9 @@
|
||||
#undef DISABLED // Redefined by ESP32
|
||||
#undef M_PI // Redefined by all
|
||||
#undef _BV // Redefined by some
|
||||
#undef sq // Redefined by teensy3/wiring.h
|
||||
#undef SBI // Redefined by arduino/const_functions.h
|
||||
#undef CBI // Redefined by arduino/const_functions.h
|
||||
#undef sq // Redefined by teensy3/wiring.h
|
||||
#undef UNUSED // Redefined by stm32f4xx_hal_def.h
|
||||
|
||||
#include <Arduino.h> // NOTE: If included earlier then this line is a NOOP
|
||||
@@ -40,18 +40,16 @@
|
||||
|
||||
#undef _BV
|
||||
#define _BV(b) (1UL << (b))
|
||||
#ifndef SBI
|
||||
#define SBI(A,B) (A |= _BV(B))
|
||||
#endif
|
||||
#ifndef CBI
|
||||
#define CBI(A,B) (A &= ~_BV(B))
|
||||
#endif
|
||||
|
||||
#undef sq
|
||||
#define sq(x) ((x)*(x))
|
||||
|
||||
#ifndef SBI
|
||||
#define SBI(A,B) (A |= (1 << (B)))
|
||||
#endif
|
||||
|
||||
#ifndef CBI
|
||||
#define CBI(A,B) (A &= ~(1 << (B)))
|
||||
#endif
|
||||
|
||||
#ifndef __AVR__
|
||||
#ifndef strchr_P // Some platforms define a macro (DUE, teensy35)
|
||||
inline const char* strchr_P(const char *s, int c) { return strchr(s,c); }
|
||||
@@ -83,3 +81,9 @@
|
||||
#ifndef UNUSED
|
||||
#define UNUSED(x) ((void)(x))
|
||||
#endif
|
||||
|
||||
#ifndef FORCE_INLINE
|
||||
#define FORCE_INLINE inline __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
#include "progmem.h"
|
||||
|
35
Marlin/src/HAL/shared/backtrace/backtrace.cpp
Executable file → Normal file
35
Marlin/src/HAL/shared/backtrace/backtrace.cpp
Executable file → Normal file
@@ -16,40 +16,41 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(__arm__) || defined(__thumb__)
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "unwinder.h"
|
||||
#include "unwmemaccess.h"
|
||||
|
||||
#include "../../../core/serial.h"
|
||||
#include "../HAL_MinSerial.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
// Dump a backtrace entry
|
||||
static bool UnwReportOut(void* ctx, const UnwReport* bte) {
|
||||
static bool UnwReportOut(void *ctx, const UnwReport *bte) {
|
||||
int *p = (int*)ctx;
|
||||
|
||||
(*p)++;
|
||||
|
||||
SERIAL_CHAR('#'); SERIAL_PRINT(*p, DEC); SERIAL_ECHOPGM(" : ");
|
||||
SERIAL_ECHOPGM(bte->name ? bte->name : "unknown"); SERIAL_ECHOPGM("@0x"); SERIAL_PRINT(bte->function, HEX);
|
||||
SERIAL_CHAR('+'); SERIAL_PRINT(bte->address - bte->function,DEC);
|
||||
SERIAL_ECHOPGM(" PC:"); SERIAL_PRINT(bte->address,HEX); SERIAL_CHAR('\n');
|
||||
const uint32_t a = bte->address, f = bte->function;
|
||||
MinSerial::TX('#'); MinSerial::TXDec(*p); MinSerial::TX(" : ");
|
||||
MinSerial::TX(bte->name?:"unknown"); MinSerial::TX('@'); MinSerial::TXHex(f);
|
||||
MinSerial::TX('+'); MinSerial::TXDec(a - f);
|
||||
MinSerial::TX(" PC:"); MinSerial::TXHex(a);
|
||||
MinSerial::TX('\n');
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef UNW_DEBUG
|
||||
void UnwPrintf(const char* format, ...) {
|
||||
void UnwPrintf(const char *format, ...) {
|
||||
char dest[256];
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
vsprintf(dest, format, argptr);
|
||||
va_end(argptr);
|
||||
TX(&dest[0]);
|
||||
MinSerial::TX(&dest[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -64,10 +65,10 @@ static const UnwindCallbacks UnwCallbacks = {
|
||||
#endif
|
||||
};
|
||||
|
||||
// Perform a backtrace to the serial port
|
||||
void backtrace() {
|
||||
|
||||
UnwindFrame btf;
|
||||
uint32_t sp = 0, lr = 0, pc = 0;
|
||||
unsigned long sp = 0, lr = 0, pc = 0;
|
||||
|
||||
// Capture the values of the registers to perform the traceback
|
||||
__asm__ __volatile__ (
|
||||
@@ -80,6 +81,12 @@ void backtrace() {
|
||||
::
|
||||
);
|
||||
|
||||
backtrace_ex(sp, lr, pc);
|
||||
}
|
||||
|
||||
void backtrace_ex(unsigned long sp, unsigned long lr, unsigned long pc) {
|
||||
UnwindFrame btf;
|
||||
|
||||
// Fill the traceback structure
|
||||
btf.sp = sp;
|
||||
btf.fp = btf.sp;
|
||||
@@ -87,7 +94,7 @@ void backtrace() {
|
||||
btf.pc = pc | 1; // Force Thumb, as CORTEX only support it
|
||||
|
||||
// Perform a backtrace
|
||||
SERIAL_ERROR_MSG("Backtrace:");
|
||||
MinSerial::TX("Backtrace:");
|
||||
int ctr = 0;
|
||||
UnwindStart(&btf, &UnwCallbacks, &ctr);
|
||||
}
|
||||
@@ -96,4 +103,4 @@ void backtrace() {
|
||||
|
||||
void backtrace() {}
|
||||
|
||||
#endif
|
||||
#endif // __arm__ || __thumb__
|
||||
|
5
Marlin/src/HAL/shared/backtrace/backtrace.h
Executable file → Normal file
5
Marlin/src/HAL/shared/backtrace/backtrace.h
Executable file → Normal file
@@ -16,10 +16,13 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// Perform a backtrace to the serial port
|
||||
void backtrace();
|
||||
|
||||
// Perform a backtrace to the serial port
|
||||
void backtrace_ex(unsigned long sp, unsigned long lr, unsigned long pc);
|
||||
|
2
Marlin/src/HAL/shared/backtrace/unwarm.cpp
Executable file → Normal file
2
Marlin/src/HAL/shared/backtrace/unwarm.cpp
Executable file → Normal file
@@ -7,7 +7,7 @@
|
||||
* for free and use it as they wish, with or without modifications, and in
|
||||
* any context, commercially or otherwise. The only limitation is that I
|
||||
* don't guarantee that the software is fit for any purpose or accept any
|
||||
* liability for it's use or misuse - this software is without warranty.
|
||||
* liability for its use or misuse - this software is without warranty.
|
||||
***************************************************************************
|
||||
* File Description: Utility functions and glue for ARM unwinding sub-modules.
|
||||
**************************************************************************/
|
||||
|
4
Marlin/src/HAL/shared/backtrace/unwarm.h
Executable file → Normal file
4
Marlin/src/HAL/shared/backtrace/unwarm.h
Executable file → Normal file
@@ -4,9 +4,9 @@
|
||||
* This program is PUBLIC DOMAIN.
|
||||
* This means that there is no copyright and anyone is able to take a copy
|
||||
* for free and use it as they wish, with or without modifications, and in
|
||||
* any context, commerically or otherwise. The only limitation is that I
|
||||
* any context, commercially or otherwise. The only limitation is that I
|
||||
* don't guarantee that the software is fit for any purpose or accept any
|
||||
* liablity for it's use or misuse - this software is without warranty.
|
||||
* liability for its use or misuse - this software is without warranty.
|
||||
***************************************************************************
|
||||
* File Description: Internal interface between the ARM unwinding sub-modules.
|
||||
**************************************************************************/
|
||||
|
7
Marlin/src/HAL/shared/backtrace/unwarm_arm.cpp
Executable file → Normal file
7
Marlin/src/HAL/shared/backtrace/unwarm_arm.cpp
Executable file → Normal file
@@ -7,7 +7,7 @@
|
||||
* for free and use it as they wish, with or without modifications, and in
|
||||
* any context, commercially or otherwise. The only limitation is that I
|
||||
* don't guarantee that the software is fit for any purpose or accept any
|
||||
* liability for it's use or misuse - this software is without warranty.
|
||||
* liability for its use or misuse - this software is without warranty.
|
||||
***************************************************************************
|
||||
* File Description: Abstract interpreter for ARM mode.
|
||||
**************************************************************************/
|
||||
@@ -43,10 +43,9 @@ static bool isDataProc(uint32_t instr) {
|
||||
}
|
||||
|
||||
UnwResult UnwStartArm(UnwState * const state) {
|
||||
bool found = false;
|
||||
uint16_t t = UNW_MAX_INSTR_COUNT;
|
||||
|
||||
do {
|
||||
for (;;) {
|
||||
uint32_t instr;
|
||||
|
||||
/* Attempt to read the instruction */
|
||||
@@ -527,7 +526,7 @@ UnwResult UnwStartArm(UnwState * const state) {
|
||||
|
||||
if (--t == 0) return UNWIND_EXHAUSTED;
|
||||
|
||||
} while (!found);
|
||||
}
|
||||
|
||||
return UNWIND_UNSUPPORTED;
|
||||
}
|
||||
|
9
Marlin/src/HAL/shared/backtrace/unwarm_thumb.cpp
Executable file → Normal file
9
Marlin/src/HAL/shared/backtrace/unwarm_thumb.cpp
Executable file → Normal file
@@ -7,7 +7,7 @@
|
||||
* for free and use it as they wish, with or without modifications, and in
|
||||
* any context, commercially or otherwise. The only limitation is that I
|
||||
* don't guarantee that the software is fit for any purpose or accept any
|
||||
* liability for it's use or misuse - this software is without warranty.
|
||||
* liability for its use or misuse - this software is without warranty.
|
||||
***************************************************************************
|
||||
* File Description: Abstract interpretation for Thumb mode.
|
||||
**************************************************************************/
|
||||
@@ -30,12 +30,11 @@ static int32_t signExtend11(const uint16_t value) {
|
||||
}
|
||||
|
||||
UnwResult UnwStartThumb(UnwState * const state) {
|
||||
bool found = false;
|
||||
uint16_t t = UNW_MAX_INSTR_COUNT;
|
||||
uint32_t lastJumpAddr = 0; // Last JUMP address, to try to detect infinite loops
|
||||
bool loopDetected = false; // If a loop was detected
|
||||
|
||||
do {
|
||||
for (;;) {
|
||||
uint16_t instr;
|
||||
|
||||
/* Attempt to read the instruction */
|
||||
@@ -807,7 +806,7 @@ UnwResult UnwStartThumb(UnwState * const state) {
|
||||
case 2: /* MOV */
|
||||
UnwPrintd5("MOV r%d, r%d\t; r%d %s", rhd, rhs, rhd, M_Origin2Str(state->regData[rhs].o));
|
||||
state->regData[rhd].v = state->regData[rhs].v;
|
||||
state->regData[rhd].o = state->regData[rhd].o;
|
||||
state->regData[rhd].o = state->regData[rhs].o;
|
||||
break;
|
||||
|
||||
case 3: /* BX */
|
||||
@@ -1059,7 +1058,7 @@ UnwResult UnwStartThumb(UnwState * const state) {
|
||||
|
||||
if (--t == 0) return UNWIND_EXHAUSTED;
|
||||
|
||||
} while (!found);
|
||||
}
|
||||
|
||||
return UNWIND_SUCCESS;
|
||||
}
|
||||
|
71
Marlin/src/HAL/shared/backtrace/unwarmbytab.cpp
Executable file → Normal file
71
Marlin/src/HAL/shared/backtrace/unwarmbytab.cpp
Executable file → Normal file
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
* file, You can obtain one at https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*
|
||||
* This library was modified, some bugs fixed, stack address validated
|
||||
* and adapted to be used in Marlin 3D printer firmware as backtracer
|
||||
@@ -128,11 +128,8 @@ static UnwResult UnwTabStateInit(const UnwindCallbacks *cb, UnwTabState *ucb, ui
|
||||
* Execute unwinding instructions
|
||||
*/
|
||||
static UnwResult UnwTabExecuteInstructions(const UnwindCallbacks *cb, UnwTabState *ucb) {
|
||||
|
||||
int instruction;
|
||||
uint32_t mask;
|
||||
uint32_t reg;
|
||||
uint32_t vsp;
|
||||
uint32_t mask, reg, vsp;
|
||||
|
||||
/* Consume all instruction byte */
|
||||
while ((instruction = UnwTabGetNextInstruction(cb, ucb)) != -1) {
|
||||
@@ -140,12 +137,12 @@ static UnwResult UnwTabExecuteInstructions(const UnwindCallbacks *cb, UnwTabStat
|
||||
if ((instruction & 0xC0) == 0x00) { // ARM_EXIDX_CMD_DATA_POP
|
||||
/* vsp = vsp + (xxxxxx << 2) + 4 */
|
||||
ucb->vrs[13] += ((instruction & 0x3F) << 2) + 4;
|
||||
} else
|
||||
if ((instruction & 0xC0) == 0x40) { // ARM_EXIDX_CMD_DATA_PUSH
|
||||
}
|
||||
else if ((instruction & 0xC0) == 0x40) { // ARM_EXIDX_CMD_DATA_PUSH
|
||||
/* vsp = vsp - (xxxxxx << 2) - 4 */
|
||||
ucb->vrs[13] -= ((instruction & 0x3F) << 2) - 4;
|
||||
} else
|
||||
if ((instruction & 0xF0) == 0x80) {
|
||||
}
|
||||
else if ((instruction & 0xF0) == 0x80) {
|
||||
/* pop under mask {r15-r12},{r11-r4} or refuse to unwind */
|
||||
instruction = instruction << 8 | UnwTabGetNextInstruction(cb, ucb);
|
||||
|
||||
@@ -171,17 +168,17 @@ static UnwResult UnwTabExecuteInstructions(const UnwindCallbacks *cb, UnwTabStat
|
||||
}
|
||||
|
||||
/* Patch up the vrs sp if it was in the mask */
|
||||
if ((instruction & (1 << (13 - 4))) != 0)
|
||||
if (instruction & (1 << (13 - 4)))
|
||||
ucb->vrs[13] = vsp;
|
||||
|
||||
} else
|
||||
if ((instruction & 0xF0) == 0x90 && // ARM_EXIDX_CMD_REG_TO_SP
|
||||
instruction != 0x9D &&
|
||||
instruction != 0x9F) {
|
||||
}
|
||||
else if ((instruction & 0xF0) == 0x90 // ARM_EXIDX_CMD_REG_TO_SP
|
||||
&& instruction != 0x9D
|
||||
&& instruction != 0x9F
|
||||
) {
|
||||
/* vsp = r[nnnn] */
|
||||
ucb->vrs[13] = ucb->vrs[instruction & 0x0F];
|
||||
} else
|
||||
if ((instruction & 0xF0) == 0xA0) { // ARM_EXIDX_CMD_REG_POP
|
||||
}
|
||||
else if ((instruction & 0xF0) == 0xA0) { // ARM_EXIDX_CMD_REG_POP
|
||||
/* pop r4-r[4+nnn] or pop r4-r[4+nnn], r14*/
|
||||
vsp = ucb->vrs[13];
|
||||
|
||||
@@ -204,8 +201,8 @@ static UnwResult UnwTabExecuteInstructions(const UnwindCallbacks *cb, UnwTabStat
|
||||
|
||||
ucb->vrs[13] = vsp;
|
||||
|
||||
} else
|
||||
if (instruction == 0xB0) { // ARM_EXIDX_CMD_FINISH
|
||||
}
|
||||
else if (instruction == 0xB0) { // ARM_EXIDX_CMD_FINISH
|
||||
/* finished */
|
||||
if (ucb->vrs[15] == 0)
|
||||
ucb->vrs[15] = ucb->vrs[14];
|
||||
@@ -213,8 +210,8 @@ static UnwResult UnwTabExecuteInstructions(const UnwindCallbacks *cb, UnwTabStat
|
||||
/* All done unwinding */
|
||||
return UNWIND_SUCCESS;
|
||||
|
||||
} else
|
||||
if (instruction == 0xB1) { // ARM_EXIDX_CMD_REG_POP
|
||||
}
|
||||
else if (instruction == 0xB1) { // ARM_EXIDX_CMD_REG_POP
|
||||
/* pop register under mask {r3,r2,r1,r0} */
|
||||
vsp = ucb->vrs[13];
|
||||
mask = UnwTabGetNextInstruction(cb, ucb);
|
||||
@@ -234,16 +231,15 @@ static UnwResult UnwTabExecuteInstructions(const UnwindCallbacks *cb, UnwTabStat
|
||||
}
|
||||
ucb->vrs[13] = (uint32_t)vsp;
|
||||
|
||||
} else
|
||||
if (instruction == 0xB2) { // ARM_EXIDX_CMD_DATA_POP
|
||||
}
|
||||
else if (instruction == 0xB2) { // ARM_EXIDX_CMD_DATA_POP
|
||||
/* vps = vsp + 0x204 + (uleb128 << 2) */
|
||||
ucb->vrs[13] += 0x204 + (UnwTabGetNextInstruction(cb, ucb) << 2);
|
||||
|
||||
} else
|
||||
if (instruction == 0xB3 || // ARM_EXIDX_CMD_VFP_POP
|
||||
instruction == 0xC8 ||
|
||||
instruction == 0xC9) {
|
||||
|
||||
}
|
||||
else if (instruction == 0xB3 // ARM_EXIDX_CMD_VFP_POP
|
||||
|| instruction == 0xC8
|
||||
|| instruction == 0xC9
|
||||
) {
|
||||
/* pop VFP double-precision registers */
|
||||
vsp = ucb->vrs[13];
|
||||
|
||||
@@ -266,27 +262,20 @@ static UnwResult UnwTabExecuteInstructions(const UnwindCallbacks *cb, UnwTabStat
|
||||
}
|
||||
|
||||
ucb->vrs[13] = vsp;
|
||||
|
||||
} else
|
||||
if ((instruction & 0xF8) == 0xB8 ||
|
||||
(instruction & 0xF8) == 0xD0) {
|
||||
|
||||
}
|
||||
else if ((instruction & 0xF8) == 0xB8 || (instruction & 0xF8) == 0xD0) {
|
||||
/* Pop VFP double precision registers D[8]-D[8+nnn] */
|
||||
ucb->vrs[14] = 0x80 | (instruction & 0x07);
|
||||
|
||||
if ((instruction & 0xF8) == 0xD0) {
|
||||
if ((instruction & 0xF8) == 0xD0)
|
||||
ucb->vrs[14] = 1 << 17;
|
||||
}
|
||||
|
||||
} else
|
||||
}
|
||||
else
|
||||
return UNWIND_UNSUPPORTED_DWARF_INSTR;
|
||||
}
|
||||
|
||||
return UNWIND_SUCCESS;
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline)) uint32_t read_psp() {
|
||||
|
||||
/* Read the current PSP and return its value as a pointer */
|
||||
uint32_t psp;
|
||||
|
||||
|
4
Marlin/src/HAL/shared/backtrace/unwarmbytab.h
Executable file → Normal file
4
Marlin/src/HAL/shared/backtrace/unwarmbytab.h
Executable file → Normal file
@@ -5,9 +5,9 @@
|
||||
* This program is PUBLIC DOMAIN.
|
||||
* This means that there is no copyright and anyone is able to take a copy
|
||||
* for free and use it as they wish, with or without modifications, and in
|
||||
* any context, commerically or otherwise. The only limitation is that I
|
||||
* any context, commercially or otherwise. The only limitation is that I
|
||||
* don't guarantee that the software is fit for any purpose or accept any
|
||||
* liablity for it's use or misuse - this software is without warranty.
|
||||
* liability for its use or misuse - this software is without warranty.
|
||||
***************************************************************************
|
||||
* File Description: Interface to the memory tracking sub-system.
|
||||
**************************************************************************/
|
||||
|
4
Marlin/src/HAL/shared/backtrace/unwarmmem.cpp
Executable file → Normal file
4
Marlin/src/HAL/shared/backtrace/unwarmmem.cpp
Executable file → Normal file
@@ -5,9 +5,9 @@
|
||||
* This program is PUBLIC DOMAIN.
|
||||
* This means that there is no copyright and anyone is able to take a copy
|
||||
* for free and use it as they wish, with or without modifications, and in
|
||||
* any context, commerically or otherwise. The only limitation is that I
|
||||
* any context, commercially or otherwise. The only limitation is that I
|
||||
* don't guarantee that the software is fit for any purpose or accept any
|
||||
* liablity for it's use or misuse - this software is without warranty.
|
||||
* liability for its use or misuse - this software is without warranty.
|
||||
***************************************************************************
|
||||
* File Description: Implementation of the memory tracking sub-system.
|
||||
**************************************************************************/
|
||||
|
4
Marlin/src/HAL/shared/backtrace/unwarmmem.h
Executable file → Normal file
4
Marlin/src/HAL/shared/backtrace/unwarmmem.h
Executable file → Normal file
@@ -5,9 +5,9 @@
|
||||
* This program is PUBLIC DOMAIN.
|
||||
* This means that there is no copyright and anyone is able to take a copy
|
||||
* for free and use it as they wish, with or without modifications, and in
|
||||
* any context, commerically or otherwise. The only limitation is that I
|
||||
* any context, commercially or otherwise. The only limitation is that I
|
||||
* don't guarantee that the software is fit for any purpose or accept any
|
||||
* liablity for it's use or misuse - this software is without warranty.
|
||||
* liability for its use or misuse - this software is without warranty.
|
||||
***************************************************************************
|
||||
* File Description: Interface to the memory tracking sub-system.
|
||||
**************************************************************************/
|
||||
|
4
Marlin/src/HAL/shared/backtrace/unwinder.cpp
Executable file → Normal file
4
Marlin/src/HAL/shared/backtrace/unwinder.cpp
Executable file → Normal file
@@ -7,7 +7,7 @@
|
||||
* for free and use it as they wish, with or without modifications, and in
|
||||
* any context, commercially or otherwise. The only limitation is that I
|
||||
* don't guarantee that the software is fit for any purpose or accept any
|
||||
* liability for it's use or misuse - this software is without warranty.
|
||||
* liability for its use or misuse - this software is without warranty.
|
||||
***************************************************************************
|
||||
* File Description: Implementation of the interface into the ARM unwinder.
|
||||
**************************************************************************/
|
||||
@@ -28,7 +28,7 @@ extern "C" const UnwTabEntry __exidx_end[];
|
||||
|
||||
// Detect if unwind information is present or not
|
||||
static int HasUnwindTableInfo() {
|
||||
// > 16 because there are default entries we can't supress
|
||||
// > 16 because there are default entries we can't suppress
|
||||
return ((char*)(&__exidx_end) - (char*)(&__exidx_start)) > 16 ? 1 : 0;
|
||||
}
|
||||
|
||||
|
6
Marlin/src/HAL/shared/backtrace/unwinder.h
Executable file → Normal file
6
Marlin/src/HAL/shared/backtrace/unwinder.h
Executable file → Normal file
@@ -5,9 +5,9 @@
|
||||
* This program is PUBLIC DOMAIN.
|
||||
* This means that there is no copyright and anyone is able to take a copy
|
||||
* for free and use it as they wish, with or without modifications, and in
|
||||
* any context, commerically or otherwise. The only limitation is that I
|
||||
* any context, commercially or otherwise. The only limitation is that I
|
||||
* don't guarantee that the software is fit for any purpose or accept any
|
||||
* liablity for it's use or misuse - this software is without warranty.
|
||||
* liability for its use or misuse - this software is without warranty.
|
||||
**************************************************************************/
|
||||
/** \file
|
||||
* Interface to the ARM stack unwinding module.
|
||||
@@ -114,7 +114,7 @@ typedef struct {
|
||||
* report function maybe called again in future. If false is returned,
|
||||
* unwinding will stop with UnwindStart() returning UNWIND_TRUNCATED.
|
||||
*/
|
||||
typedef bool (*UnwindReportFunc)(void* data, const UnwReport* bte);
|
||||
typedef bool (*UnwindReportFunc)(void *data, const UnwReport *bte);
|
||||
|
||||
/** Structure that holds memory callback function pointers.
|
||||
*/
|
||||
|
292
Marlin/src/HAL/shared/backtrace/unwmemaccess.cpp
Executable file → Normal file
292
Marlin/src/HAL/shared/backtrace/unwmemaccess.cpp
Executable file → Normal file
@@ -7,7 +7,7 @@
|
||||
* for free and use it as they wish, with or without modifications, and in
|
||||
* any context, commercially or otherwise. The only limitation is that I
|
||||
* don't guarantee that the software is fit for any purpose or accept any
|
||||
* liability for it's use or misuse - this software is without warranty.
|
||||
* liability for its use or misuse - this software is without warranty.
|
||||
***************************************************************************
|
||||
* File Description: Utility functions to access memory
|
||||
**************************************************************************/
|
||||
@@ -20,139 +20,169 @@
|
||||
/* Validate address */
|
||||
|
||||
#ifdef ARDUINO_ARCH_SAM
|
||||
// For DUE, valid address ranges are
|
||||
// SRAM (0x20070000 - 0x20088000) (96kb)
|
||||
// FLASH (0x00080000 - 0x00100000) (512kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x20070000
|
||||
#define END_SRAM_ADDR 0x20088000
|
||||
#define START_FLASH_ADDR 0x00080000
|
||||
#define END_FLASH_ADDR 0x00100000
|
||||
|
||||
// For DUE, valid address ranges are
|
||||
// SRAM (0x20070000 - 0x20088000) (96kb)
|
||||
// FLASH (0x00080000 - 0x00100000) (512kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x20070000
|
||||
#define END_SRAM_ADDR 0x20088000
|
||||
#define START_FLASH_ADDR 0x00080000
|
||||
#define END_FLASH_ADDR 0x00100000
|
||||
|
||||
#elif defined(TARGET_LPC1768)
|
||||
|
||||
// For LPC1769:
|
||||
// SRAM (0x10000000 - 0x10008000) (32kb)
|
||||
// FLASH (0x00000000 - 0x00080000) (512kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x10000000
|
||||
#define END_SRAM_ADDR 0x10008000
|
||||
#define START_FLASH_ADDR 0x00000000
|
||||
#define END_FLASH_ADDR 0x00080000
|
||||
|
||||
#elif defined(__STM32F1__) || defined(STM32F1xx) || defined(STM32F0xx)
|
||||
|
||||
// For STM32F103ZET6/STM32F103VET6/STM32F0xx
|
||||
// SRAM (0x20000000 - 0x20010000) (64kb)
|
||||
// FLASH (0x08000000 - 0x08080000) (512kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x20000000
|
||||
#define END_SRAM_ADDR 0x20010000
|
||||
#define START_FLASH_ADDR 0x08000000
|
||||
#define END_FLASH_ADDR 0x08080000
|
||||
|
||||
#elif defined(STM32F4) || defined(STM32F4xx)
|
||||
|
||||
// For STM32F407VET
|
||||
// SRAM (0x20000000 - 0x20030000) (192kb)
|
||||
// FLASH (0x08000000 - 0x08080000) (512kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x20000000
|
||||
#define END_SRAM_ADDR 0x20030000
|
||||
#define START_FLASH_ADDR 0x08000000
|
||||
#define END_FLASH_ADDR 0x08080000
|
||||
|
||||
#elif MB(REMRAM_V1, NUCLEO_F767ZI)
|
||||
|
||||
// For STM32F765VI in RemRam v1
|
||||
// SRAM (0x20000000 - 0x20080000) (512kb)
|
||||
// FLASH (0x08000000 - 0x08200000) (2048kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x20000000
|
||||
#define END_SRAM_ADDR 0x20080000
|
||||
#define START_FLASH_ADDR 0x08000000
|
||||
#define END_FLASH_ADDR 0x08200000
|
||||
|
||||
#elif defined(__MK20DX256__)
|
||||
|
||||
// For MK20DX256 in TEENSY 3.1 or TEENSY 3.2
|
||||
// SRAM (0x1FFF8000 - 0x20008000) (64kb)
|
||||
// FLASH (0x00000000 - 0x00040000) (256kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x1FFF8000
|
||||
#define END_SRAM_ADDR 0x20008000
|
||||
#define START_FLASH_ADDR 0x00000000
|
||||
#define END_FLASH_ADDR 0x00040000
|
||||
|
||||
#elif defined(__MK64FX512__)
|
||||
|
||||
// For MK64FX512 in TEENSY 3.5
|
||||
// SRAM (0x1FFF0000 - 0x20020000) (192kb)
|
||||
// FLASH (0x00000000 - 0x00080000) (512kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x1FFF0000
|
||||
#define END_SRAM_ADDR 0x20020000
|
||||
#define START_FLASH_ADDR 0x00000000
|
||||
#define END_FLASH_ADDR 0x00080000
|
||||
|
||||
#elif defined(__MK66FX1M0__)
|
||||
|
||||
// For MK66FX1M0 in TEENSY 3.6
|
||||
// SRAM (0x1FFF0000 - 0x20030000) (256kb)
|
||||
// FLASH (0x00000000 - 0x00140000) (1.25Mb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x1FFF0000
|
||||
#define END_SRAM_ADDR 0x20030000
|
||||
#define START_FLASH_ADDR 0x00000000
|
||||
#define END_FLASH_ADDR 0x00140000
|
||||
|
||||
#elif defined(__IMXRT1062__)
|
||||
|
||||
// For IMXRT1062 in TEENSY 4.0/4/1
|
||||
// ITCM (rwx): ORIGIN = 0x00000000, LENGTH = 512K
|
||||
// DTCM (rwx): ORIGIN = 0x20000000, LENGTH = 512K
|
||||
// RAM (rwx): ORIGIN = 0x20200000, LENGTH = 512K
|
||||
// FLASH (rwx): ORIGIN = 0x60000000, LENGTH = 1984K
|
||||
//
|
||||
#define START_SRAM_ADDR 0x00000000
|
||||
#define END_SRAM_ADDR 0x20280000
|
||||
#define START_FLASH_ADDR 0x60000000
|
||||
#define END_FLASH_ADDR 0x601F0000
|
||||
|
||||
#elif defined(__SAMD51P20A__)
|
||||
|
||||
// For SAMD51x20, valid address ranges are
|
||||
// SRAM (0x20000000 - 0x20040000) (256kb)
|
||||
// FLASH (0x00000000 - 0x00100000) (1024kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x20000000
|
||||
#define END_SRAM_ADDR 0x20040000
|
||||
#define START_FLASH_ADDR 0x00000000
|
||||
#define END_FLASH_ADDR 0x00100000
|
||||
|
||||
#else
|
||||
// Generic ARM code, that's testing if an access to the given address would cause a fault or not
|
||||
// It can't guarantee an address is in RAM or Flash only, but we usually don't care
|
||||
|
||||
#define NVIC_FAULT_STAT 0xE000ED28 // Configurable Fault Status Reg.
|
||||
#define NVIC_CFG_CTRL 0xE000ED14 // Configuration Control Register
|
||||
#define NVIC_FAULT_STAT_BFARV 0x00008000 // BFAR is valid
|
||||
#define NVIC_CFG_CTRL_BFHFNMIGN 0x00000100 // Ignore bus fault in NMI/fault
|
||||
#define HW_REG(X) (*((volatile unsigned long *)(X)))
|
||||
|
||||
static bool validate_addr(uint32_t read_address) {
|
||||
bool works = true;
|
||||
uint32_t intDisabled = 0;
|
||||
// Read current interrupt state
|
||||
__asm__ __volatile__ ("MRS %[result], PRIMASK\n\t" : [result]"=r"(intDisabled) :: ); // 0 is int enabled, 1 for disabled
|
||||
|
||||
// Clear bus fault indicator first (write 1 to clear)
|
||||
HW_REG(NVIC_FAULT_STAT) |= NVIC_FAULT_STAT_BFARV;
|
||||
// Ignore bus fault interrupt
|
||||
HW_REG(NVIC_CFG_CTRL) |= NVIC_CFG_CTRL_BFHFNMIGN;
|
||||
// Disable interrupts if not disabled previously
|
||||
if (!intDisabled) __asm__ __volatile__ ("CPSID f");
|
||||
// Probe address
|
||||
*(volatile uint32_t*)read_address;
|
||||
// Check if a fault happened
|
||||
if ((HW_REG(NVIC_FAULT_STAT) & NVIC_FAULT_STAT_BFARV) != 0)
|
||||
works = false;
|
||||
// Enable interrupts again if previously disabled
|
||||
if (!intDisabled) __asm__ __volatile__ ("CPSIE f");
|
||||
// Enable fault interrupt flag
|
||||
HW_REG(NVIC_CFG_CTRL) &= ~NVIC_CFG_CTRL_BFHFNMIGN;
|
||||
|
||||
return works;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_LPC1768
|
||||
// For LPC1769:
|
||||
// SRAM (0x10000000 - 0x10008000) (32kb)
|
||||
// FLASH (0x00000000 - 0x00080000) (512kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x10000000
|
||||
#define END_SRAM_ADDR 0x10008000
|
||||
#define START_FLASH_ADDR 0x00000000
|
||||
#define END_FLASH_ADDR 0x00080000
|
||||
#ifdef START_SRAM_ADDR
|
||||
static bool validate_addr(uint32_t addr) {
|
||||
|
||||
// Address must be in SRAM range
|
||||
if (addr >= START_SRAM_ADDR && addr < END_SRAM_ADDR)
|
||||
return true;
|
||||
|
||||
// Or in FLASH range
|
||||
if (addr >= START_FLASH_ADDR && addr < END_FLASH_ADDR)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// For STM32F103CBT6
|
||||
// SRAM (0x20000000 - 0x20005000) (20kb)
|
||||
// FLASH (0x00000000 - 0x00020000) (128kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x20000000
|
||||
#define END_SRAM_ADDR 0x20005000
|
||||
#define START_FLASH_ADDR 0x00000000
|
||||
#define END_FLASH_ADDR 0x00020000
|
||||
#endif
|
||||
|
||||
#if defined(__STM32F1__) || defined(STM32F1xx) || defined(STM32F0xx)
|
||||
// For STM32F103ZET6/STM32F103VET6/STM32F0xx
|
||||
// SRAM (0x20000000 - 0x20010000) (64kb)
|
||||
// FLASH (0x00000000 - 0x00080000) (512kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x20000000
|
||||
#define END_SRAM_ADDR 0x20010000
|
||||
#define START_FLASH_ADDR 0x00000000
|
||||
#define END_FLASH_ADDR 0x00080000
|
||||
#endif
|
||||
|
||||
#if defined(STM32F4) || defined(STM32F4xx)
|
||||
// For STM32F407VET
|
||||
// SRAM (0x20000000 - 0x20030000) (192kb)
|
||||
// FLASH (0x08000000 - 0x08080000) (512kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x20000000
|
||||
#define END_SRAM_ADDR 0x20030000
|
||||
#define START_FLASH_ADDR 0x08000000
|
||||
#define END_FLASH_ADDR 0x08080000
|
||||
#endif
|
||||
|
||||
#if MB(THE_BORG)
|
||||
// For STM32F765 in BORG
|
||||
// SRAM (0x20000000 - 0x20080000) (512kb)
|
||||
// FLASH (0x08000000 - 0x08100000) (1024kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x20000000
|
||||
#define END_SRAM_ADDR 0x20080000
|
||||
#define START_FLASH_ADDR 0x08000000
|
||||
#define END_FLASH_ADDR 0x08100000
|
||||
#endif
|
||||
|
||||
#if MB(REMRAM_V1)
|
||||
// For STM32F765VI in RemRam v1
|
||||
// SRAM (0x20000000 - 0x20080000) (512kb)
|
||||
// FLASH (0x08000000 - 0x08200000) (2048kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x20000000
|
||||
#define END_SRAM_ADDR 0x20080000
|
||||
#define START_FLASH_ADDR 0x08000000
|
||||
#define END_FLASH_ADDR 0x08200000
|
||||
#endif
|
||||
|
||||
#ifdef __MK20DX256__
|
||||
// For MK20DX256 in TEENSY 3.1 or TEENSY 3.2
|
||||
// SRAM (0x1FFF8000 - 0x20008000) (64kb)
|
||||
// FLASH (0x00000000 - 0x00040000) (256kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x1FFF8000
|
||||
#define END_SRAM_ADDR 0x20008000
|
||||
#define START_FLASH_ADDR 0x00000000
|
||||
#define END_FLASH_ADDR 0x00040000
|
||||
#endif
|
||||
|
||||
#ifdef __MK64FX512__
|
||||
// For MK64FX512 in TEENSY 3.5
|
||||
// SRAM (0x1FFF0000 - 0x20020000) (192kb)
|
||||
// FLASH (0x00000000 - 0x00080000) (512kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x1FFF0000
|
||||
#define END_SRAM_ADDR 0x20020000
|
||||
#define START_FLASH_ADDR 0x00000000
|
||||
#define END_FLASH_ADDR 0x00080000
|
||||
#endif
|
||||
|
||||
#ifdef __MK66FX1M0__
|
||||
// For MK66FX1M0 in TEENSY 3.6
|
||||
// SRAM (0x1FFF0000 - 0x20030000) (256kb)
|
||||
// FLASH (0x00000000 - 0x00140000) (1.25Mb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x1FFF0000
|
||||
#define END_SRAM_ADDR 0x20030000
|
||||
#define START_FLASH_ADDR 0x00000000
|
||||
#define END_FLASH_ADDR 0x00140000
|
||||
#endif
|
||||
|
||||
#ifdef __SAMD51P20A__
|
||||
// For SAMD51x20, valid address ranges are
|
||||
// SRAM (0x20000000 - 0x20040000) (256kb)
|
||||
// FLASH (0x00000000 - 0x00100000) (1024kb)
|
||||
//
|
||||
#define START_SRAM_ADDR 0x20000000
|
||||
#define END_SRAM_ADDR 0x20040000
|
||||
#define START_FLASH_ADDR 0x00000000
|
||||
#define END_FLASH_ADDR 0x00100000
|
||||
#endif
|
||||
|
||||
static bool validate_addr(uint32_t addr) {
|
||||
|
||||
// Address must be in SRAM range
|
||||
if (addr >= START_SRAM_ADDR && addr < END_SRAM_ADDR)
|
||||
return true;
|
||||
|
||||
// Or in FLASH range
|
||||
if (addr >= START_FLASH_ADDR && addr < END_FLASH_ADDR)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnwReadW(const uint32_t a, uint32_t *v) {
|
||||
if (!validate_addr(a))
|
||||
return false;
|
||||
@@ -177,4 +207,4 @@ bool UnwReadB(const uint32_t a, uint8_t *v) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // __arm__ || __thumb__
|
||||
|
4
Marlin/src/HAL/shared/backtrace/unwmemaccess.h
Executable file → Normal file
4
Marlin/src/HAL/shared/backtrace/unwmemaccess.h
Executable file → Normal file
@@ -5,9 +5,9 @@
|
||||
* This program is PUBLIC DOMAIN.
|
||||
* This means that there is no copyright and anyone is able to take a copy
|
||||
* for free and use it as they wish, with or without modifications, and in
|
||||
* any context, commerically or otherwise. The only limitation is that I
|
||||
* any context, commercially or otherwise. The only limitation is that I
|
||||
* don't guarantee that the software is fit for any purpose or accept any
|
||||
* liablity for it's use or misuse - this software is without warranty.
|
||||
* liability for its use or misuse - this software is without warranty.
|
||||
***************************************************************************
|
||||
* File Description: Utility functions to access memory
|
||||
**************************************************************************/
|
||||
|
379
Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp
Normal file
379
Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp
Normal file
@@ -0,0 +1,379 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
* Copyright (c) 2020 Cyril Russo
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/***************************************************************************
|
||||
* ARM CPU Exception handler
|
||||
***************************************************************************/
|
||||
|
||||
#if defined(__arm__) || defined(__thumb__)
|
||||
|
||||
|
||||
/*
|
||||
On ARM CPUs exception handling is quite powerful.
|
||||
|
||||
By default, upon a crash, the CPU enters the handlers that have a higher priority than any other interrupts,
|
||||
so, in effect, no (real) interrupt can "interrupt" the handler (it's acting like if interrupts were disabled).
|
||||
|
||||
If the handler is not called as re-entrant (that is, if the crash is not happening inside an interrupt or an handler),
|
||||
then it'll patch the return address to a dumping function (resume_from_fault) and save the crash state.
|
||||
The CPU will exit the handler and, as such, re-allow the other interrupts, and jump to the dumping function.
|
||||
In this function, the usual serial port (USB / HW) will be used to dump the crash (no special configuration required).
|
||||
|
||||
The only case where it requires hardware UART is when it's crashing in an interrupt or a crash handler.
|
||||
In that case, instead of returning to the resume_from_fault function (and thus, re-enabling interrupts),
|
||||
it jumps to this function directly (so with interrupts disabled), after changing the behavior of the serial output
|
||||
wrapper to use the HW uart (and in effect, calling MinSerial::init which triggers a warning if you are using
|
||||
a USB serial port).
|
||||
|
||||
In the case you have a USB serial port, this part will be disabled, and only that part (so that's the reason for
|
||||
the warning).
|
||||
This means that you can't have a crash report if the crash happens in an interrupt or an handler if you are using
|
||||
a USB serial port since it's physically impossible.
|
||||
You will get a crash report in all other cases.
|
||||
*/
|
||||
|
||||
#include "exception_hook.h"
|
||||
#include "../backtrace/backtrace.h"
|
||||
#include "../HAL_MinSerial.h"
|
||||
|
||||
#define HW_REG(X) (*((volatile unsigned long *)(X)))
|
||||
|
||||
// Default function use the CPU VTOR register to get the vector table.
|
||||
// Accessing the CPU VTOR register is done in assembly since it's the only way that's common to all current tool
|
||||
unsigned long get_vtor() { return HW_REG(0xE000ED08); } // Even if it looks like an error, it is not an error
|
||||
void * hook_get_hardfault_vector_address(unsigned vtor) { return (void*)(vtor + 0x03); }
|
||||
void * hook_get_memfault_vector_address(unsigned vtor) { return (void*)(vtor + 0x04); }
|
||||
void * hook_get_busfault_vector_address(unsigned vtor) { return (void*)(vtor + 0x05); }
|
||||
void * hook_get_usagefault_vector_address(unsigned vtor) { return (void*)(vtor + 0x06); }
|
||||
void * hook_get_reserved_vector_address(unsigned vtor) { return (void*)(vtor + 0x07); }
|
||||
|
||||
// Common exception frame for ARM, should work for all ARM CPU
|
||||
// Described here (modified for convenience): https://interrupt.memfault.com/blog/cortex-m-fault-debug
|
||||
struct __attribute__((packed)) ContextStateFrame {
|
||||
uint32_t r0;
|
||||
uint32_t r1;
|
||||
uint32_t r2;
|
||||
uint32_t r3;
|
||||
uint32_t r12;
|
||||
uint32_t lr;
|
||||
uint32_t pc;
|
||||
uint32_t xpsr;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) ContextSavedFrame {
|
||||
uint32_t R0;
|
||||
uint32_t R1;
|
||||
uint32_t R2;
|
||||
uint32_t R3;
|
||||
uint32_t R12;
|
||||
uint32_t LR;
|
||||
uint32_t PC;
|
||||
uint32_t XPSR;
|
||||
|
||||
uint32_t CFSR;
|
||||
uint32_t HFSR;
|
||||
uint32_t DFSR;
|
||||
uint32_t AFSR;
|
||||
uint32_t MMAR;
|
||||
uint32_t BFAR;
|
||||
|
||||
uint32_t ESP;
|
||||
uint32_t ELR;
|
||||
};
|
||||
|
||||
#if DISABLED(STM32F0xx)
|
||||
extern "C"
|
||||
__attribute__((naked)) void CommonHandler_ASM() {
|
||||
__asm__ __volatile__ (
|
||||
// Bit 2 of LR tells which stack pointer to use (either main or process, only main should be used anyway)
|
||||
"tst lr, #4\n"
|
||||
"ite eq\n"
|
||||
"mrseq r0, msp\n"
|
||||
"mrsne r0, psp\n"
|
||||
// Save the LR in use when being interrupted
|
||||
"mov r1, lr\n"
|
||||
// Get the exception number from the ICSR register
|
||||
"ldr r2, =0xE000ED00\n"
|
||||
"ldr r2, [r2, #4]\n"
|
||||
"b CommonHandler_C\n"
|
||||
);
|
||||
}
|
||||
#else // Cortex M0 does not support conditional mov and testing with a constant, so let's have a specific handler for it
|
||||
extern "C"
|
||||
__attribute__((naked)) void CommonHandler_ASM() {
|
||||
__asm__ __volatile__ (
|
||||
".syntax unified\n"
|
||||
// Save the LR in use when being interrupted
|
||||
"mov r1, lr\n"
|
||||
// Get the exception number from the ICSR register
|
||||
"ldr r2, =0xE000ED00\n"
|
||||
"ldr r2, [r2, #4]\n"
|
||||
"movs r0, #4\n"
|
||||
"tst r1, r0\n"
|
||||
"beq _MSP\n"
|
||||
"mrs r0, psp\n"
|
||||
"b CommonHandler_C\n"
|
||||
"_MSP:\n"
|
||||
"mrs r0, msp\n"
|
||||
"b CommonHandler_C\n"
|
||||
);
|
||||
}
|
||||
|
||||
#if DISABLED(DYNAMIC_VECTORTABLE) // Cortex M0 requires the handler's address to be within 32kB to the actual function to be able to branch to it
|
||||
extern "C" {
|
||||
void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_hardfault();
|
||||
void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_busfault();
|
||||
void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_usagefault();
|
||||
void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_memmanage();
|
||||
void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_nmi();
|
||||
void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception7();
|
||||
void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception8();
|
||||
void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception9();
|
||||
void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception10();
|
||||
void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception13();
|
||||
}
|
||||
//TODO When going off from libmaple, you'll need to replace those by the one from STM32/HAL_MinSerial.cpp
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Must be a macro to avoid creating a function frame
|
||||
#define HALT_IF_DEBUGGING() \
|
||||
do { \
|
||||
if (HW_REG(0xE000EDF0) & _BV(0)) { \
|
||||
__asm("bkpt 1"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Resume from a fault (if possible) so we can still use interrupt based code for serial output
|
||||
// In that case, we will not jump back to the faulty code, but instead to a dumping code and then a
|
||||
// basic loop with watchdog calling or manual resetting
|
||||
static ContextSavedFrame savedFrame;
|
||||
static uint8_t lastCause;
|
||||
bool resume_from_fault() {
|
||||
static const char* causestr[] = { "Thread", "Rsvd", "NMI", "Hard", "Mem", "Bus", "Usage", "7", "8", "9", "10", "SVC", "Dbg", "13", "PendSV", "SysTk", "IRQ" };
|
||||
// Reinit the serial link (might only work if implemented in each of your boards)
|
||||
MinSerial::init();
|
||||
|
||||
MinSerial::TX("\n\n## Software Fault detected ##\n");
|
||||
MinSerial::TX("Cause: "); MinSerial::TX(causestr[min(lastCause, (uint8_t)16)]); MinSerial::TX('\n');
|
||||
|
||||
MinSerial::TX("R0 : "); MinSerial::TXHex(savedFrame.R0); MinSerial::TX('\n');
|
||||
MinSerial::TX("R1 : "); MinSerial::TXHex(savedFrame.R1); MinSerial::TX('\n');
|
||||
MinSerial::TX("R2 : "); MinSerial::TXHex(savedFrame.R2); MinSerial::TX('\n');
|
||||
MinSerial::TX("R3 : "); MinSerial::TXHex(savedFrame.R3); MinSerial::TX('\n');
|
||||
MinSerial::TX("R12 : "); MinSerial::TXHex(savedFrame.R12); MinSerial::TX('\n');
|
||||
MinSerial::TX("LR : "); MinSerial::TXHex(savedFrame.LR); MinSerial::TX('\n');
|
||||
MinSerial::TX("PC : "); MinSerial::TXHex(savedFrame.PC); MinSerial::TX('\n');
|
||||
MinSerial::TX("PSR : "); MinSerial::TXHex(savedFrame.XPSR); MinSerial::TX('\n');
|
||||
|
||||
// Configurable Fault Status Register
|
||||
// Consists of MMSR, BFSR and UFSR
|
||||
MinSerial::TX("CFSR : "); MinSerial::TXHex(savedFrame.CFSR); MinSerial::TX('\n');
|
||||
|
||||
// Hard Fault Status Register
|
||||
MinSerial::TX("HFSR : "); MinSerial::TXHex(savedFrame.HFSR); MinSerial::TX('\n');
|
||||
|
||||
// Debug Fault Status Register
|
||||
MinSerial::TX("DFSR : "); MinSerial::TXHex(savedFrame.DFSR); MinSerial::TX('\n');
|
||||
|
||||
// Auxiliary Fault Status Register
|
||||
MinSerial::TX("AFSR : "); MinSerial::TXHex(savedFrame.AFSR); MinSerial::TX('\n');
|
||||
|
||||
// Read the Fault Address Registers. These may not contain valid values.
|
||||
// Check BFARVALID/MMARVALID to see if they are valid values
|
||||
// MemManage Fault Address Register
|
||||
MinSerial::TX("MMAR : "); MinSerial::TXHex(savedFrame.MMAR); MinSerial::TX('\n');
|
||||
|
||||
// Bus Fault Address Register
|
||||
MinSerial::TX("BFAR : "); MinSerial::TXHex(savedFrame.BFAR); MinSerial::TX('\n');
|
||||
|
||||
MinSerial::TX("ExcLR: "); MinSerial::TXHex(savedFrame.ELR); MinSerial::TX('\n');
|
||||
MinSerial::TX("ExcSP: "); MinSerial::TXHex(savedFrame.ESP); MinSerial::TX('\n');
|
||||
|
||||
// The stack pointer is pushed by 8 words upon entering an exception, so we need to revert this
|
||||
backtrace_ex(savedFrame.ESP + 8*4, savedFrame.LR, savedFrame.PC);
|
||||
|
||||
// Call the last resort function here
|
||||
hook_last_resort_func();
|
||||
|
||||
const uint32_t start = millis(), end = start + 100; // 100ms should be enough
|
||||
// We need to wait for the serial buffers to be output but we don't know for how long
|
||||
// So we'll just need to refresh the watchdog for a while and then stop for the system to reboot
|
||||
uint32_t last = start;
|
||||
while (PENDING(last, end)) {
|
||||
watchdog_refresh();
|
||||
while (millis() == last) { /* nada */ }
|
||||
last = millis();
|
||||
MinSerial::TX('.');
|
||||
}
|
||||
|
||||
// Reset now by reinstantiating the bootloader's vector table
|
||||
HW_REG(0xE000ED08) = 0;
|
||||
// Restart watchdog
|
||||
#if DISABLED(USE_WATCHDOG)
|
||||
// No watchdog, let's perform ARMv7 reset instead by writing to AIRCR register with VECTKEY set to SYSRESETREQ
|
||||
HW_REG(0xE000ED0C) = (HW_REG(0xE000ED0C) & 0x0000FFFF) | 0x05FA0004;
|
||||
#endif
|
||||
|
||||
while(1) {} // Bad luck, nothing worked
|
||||
}
|
||||
|
||||
// Make sure the compiler does not optimize the frame argument away
|
||||
extern "C"
|
||||
__attribute__((optimize("O0")))
|
||||
void CommonHandler_C(ContextStateFrame * frame, unsigned long lr, unsigned long cause) {
|
||||
|
||||
// If you are using it'll stop here
|
||||
HALT_IF_DEBUGGING();
|
||||
|
||||
// Save the state to backtrace later on (don't call memcpy here since the stack can be corrupted)
|
||||
savedFrame.R0 = frame->r0;
|
||||
savedFrame.R1 = frame->r1;
|
||||
savedFrame.R2 = frame->r2;
|
||||
savedFrame.R3 = frame->r3;
|
||||
savedFrame.R12 = frame->r12;
|
||||
savedFrame.LR = frame->lr;
|
||||
savedFrame.PC = frame->pc;
|
||||
savedFrame.XPSR= frame->xpsr;
|
||||
lastCause = cause & 0x1FF;
|
||||
|
||||
volatile uint32_t &CFSR = HW_REG(0xE000ED28);
|
||||
savedFrame.CFSR = CFSR;
|
||||
savedFrame.HFSR = HW_REG(0xE000ED2C);
|
||||
savedFrame.DFSR = HW_REG(0xE000ED30);
|
||||
savedFrame.AFSR = HW_REG(0xE000ED3C);
|
||||
savedFrame.MMAR = HW_REG(0xE000ED34);
|
||||
savedFrame.BFAR = HW_REG(0xE000ED38);
|
||||
savedFrame.ESP = (unsigned long)frame; // Even on return, this should not be overwritten by the CPU
|
||||
savedFrame.ELR = lr;
|
||||
|
||||
// First check if we can resume from this exception to our own handler safely
|
||||
// If we can, then we don't need to disable interrupts and the usual serial code
|
||||
// can be used
|
||||
|
||||
//const uint32_t non_usage_fault_mask = 0x0000FFFF;
|
||||
//const bool non_usage_fault_occurred = (CFSR & non_usage_fault_mask) != 0;
|
||||
// the bottom 8 bits of the xpsr hold the exception number of the
|
||||
// executing exception or 0 if the processor is in Thread mode
|
||||
const bool faulted_from_exception = ((frame->xpsr & 0xFF) != 0);
|
||||
if (!faulted_from_exception) { // Not sure about the non_usage_fault, we want to try anyway, don't we ? && !non_usage_fault_occurred)
|
||||
// Try to resume to our handler here
|
||||
CFSR |= CFSR; // The ARM programmer manual says you must write to 1 all fault bits to clear them so this instruction is correct
|
||||
// The frame will not be valid when returning anymore, let's clean it
|
||||
savedFrame.CFSR = 0;
|
||||
|
||||
frame->pc = (uint32_t)resume_from_fault; // Patch where to return to
|
||||
frame->lr = 0xDEADBEEF; // If our handler returns (it shouldn't), let's make it trigger an exception immediately
|
||||
frame->xpsr = _BV(24); // Need to clean the PSR register to thumb II only
|
||||
MinSerial::force_using_default_output = true;
|
||||
return; // The CPU will resume in our handler hopefully, and we'll try to use default serial output
|
||||
}
|
||||
|
||||
// Sorry, we need to emergency code here since the fault is too dangerous to recover from
|
||||
MinSerial::force_using_default_output = false;
|
||||
resume_from_fault();
|
||||
}
|
||||
|
||||
void hook_cpu_exceptions() {
|
||||
#if ENABLED(DYNAMIC_VECTORTABLE)
|
||||
// On ARM 32bits CPU, the vector table is like this:
|
||||
// 0x0C => Hardfault
|
||||
// 0x10 => MemFault
|
||||
// 0x14 => BusFault
|
||||
// 0x18 => UsageFault
|
||||
|
||||
// Unfortunately, it's usually run from flash, and we can't write to flash here directly to hook our instruction
|
||||
// We could set an hardware breakpoint, and hook on the fly when it's being called, but this
|
||||
// is hard to get right and would probably break debugger when attached
|
||||
|
||||
// So instead, we'll allocate a new vector table filled with the previous value except
|
||||
// for the fault we are interested in.
|
||||
// Now, comes the issue to figure out what is the current vector table size
|
||||
// There is nothing telling us what is the vector table as it's per-cpu vendor specific.
|
||||
// BUT: we are being called at the end of the setup, so we assume the setup is done
|
||||
// Thus, we can read the current vector table until we find an address that's not in flash, and it would mark the
|
||||
// end of the vector table (skipping the fist entry obviously)
|
||||
// The position of the program in flash is expected to be at 0x08xxx xxxx on all known platform for ARM and the
|
||||
// flash size is available via register 0x1FFFF7E0 on STM32 family, but it's not the case for all ARM boards
|
||||
// (accessing this register might trigger a fault if it's not implemented).
|
||||
|
||||
// So we'll simply mask the top 8 bits of the first handler as an hint of being in the flash or not -that's poor and will
|
||||
// probably break if the flash happens to be more than 128MB, but in this case, we are not magician, we need help from outside.
|
||||
|
||||
unsigned long *vecAddr = (unsigned long*)get_vtor();
|
||||
SERIAL_ECHOPGM("Vector table addr: ");
|
||||
SERIAL_PRINTLN(get_vtor(), PrintBase::Hex);
|
||||
|
||||
#ifdef VECTOR_TABLE_SIZE
|
||||
uint32_t vec_size = VECTOR_TABLE_SIZE;
|
||||
alignas(128) static unsigned long vectable[VECTOR_TABLE_SIZE] ;
|
||||
#else
|
||||
#ifndef IS_IN_FLASH
|
||||
#define IS_IN_FLASH(X) (((unsigned long)X & 0xFF000000) == 0x08000000)
|
||||
#endif
|
||||
|
||||
// When searching for the end of the vector table, this acts as a limit not to overcome
|
||||
#ifndef VECTOR_TABLE_SENTINEL
|
||||
#define VECTOR_TABLE_SENTINEL 80
|
||||
#endif
|
||||
|
||||
// Find the vector table size
|
||||
uint32_t vec_size = 1;
|
||||
while (IS_IN_FLASH(vecAddr[vec_size]) && vec_size < VECTOR_TABLE_SENTINEL)
|
||||
vec_size++;
|
||||
|
||||
// We failed to find a valid vector table size, let's abort hooking up
|
||||
if (vec_size == VECTOR_TABLE_SENTINEL) return;
|
||||
// Poor method that's wasting RAM here, but allocating with malloc and alignment would be worst
|
||||
// 128 bytes alignment is required for writing the VTOR register
|
||||
alignas(128) static unsigned long vectable[VECTOR_TABLE_SENTINEL];
|
||||
|
||||
SERIAL_ECHOPGM("Detected vector table size: ");
|
||||
SERIAL_PRINTLN(vec_size, PrintBase::Hex);
|
||||
#endif
|
||||
|
||||
uint32_t defaultFaultHandler = vecAddr[(unsigned)7];
|
||||
// Copy the current vector table into the new table
|
||||
for (uint32_t i = 0; i < vec_size; i++) {
|
||||
vectable[i] = vecAddr[i];
|
||||
// Replace all default handler by our own handler
|
||||
if (vectable[i] == defaultFaultHandler)
|
||||
vectable[i] = (unsigned long)&CommonHandler_ASM;
|
||||
}
|
||||
|
||||
// Let's hook now with our functions
|
||||
vectable[(unsigned long)hook_get_hardfault_vector_address(0)] = (unsigned long)&CommonHandler_ASM;
|
||||
vectable[(unsigned long)hook_get_memfault_vector_address(0)] = (unsigned long)&CommonHandler_ASM;
|
||||
vectable[(unsigned long)hook_get_busfault_vector_address(0)] = (unsigned long)&CommonHandler_ASM;
|
||||
vectable[(unsigned long)hook_get_usagefault_vector_address(0)] = (unsigned long)&CommonHandler_ASM;
|
||||
|
||||
// Finally swap with our own vector table
|
||||
// This is supposed to be atomic, but let's do that with interrupt disabled
|
||||
|
||||
HW_REG(0xE000ED08) = (unsigned long)vectable | _BV32(29); // 29th bit is for telling the CPU the table is now in SRAM (should be present already)
|
||||
|
||||
SERIAL_ECHOLNPGM("Installed fault handlers");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // __arm__ || __thumb__
|
28
Marlin/src/HAL/shared/cpu_exception/exception_hook.cpp
Normal file
28
Marlin/src/HAL/shared/cpu_exception/exception_hook.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "exception_hook.h"
|
||||
|
||||
void * __attribute__((weak)) hook_get_hardfault_vector_address(unsigned) { return 0; }
|
||||
void * __attribute__((weak)) hook_get_memfault_vector_address(unsigned) { return 0; }
|
||||
void * __attribute__((weak)) hook_get_busfault_vector_address(unsigned) { return 0; }
|
||||
void * __attribute__((weak)) hook_get_usagefault_vector_address(unsigned) { return 0; }
|
||||
void __attribute__((weak)) hook_last_resort_func() {}
|
54
Marlin/src/HAL/shared/cpu_exception/exception_hook.h
Normal file
54
Marlin/src/HAL/shared/cpu_exception/exception_hook.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/* Here is the expected behavior of a system producing a CPU exception with this hook installed:
|
||||
1. Before the system is crashed
|
||||
1.1 Upon validation (not done yet in this code, but we could be using DEBUG flags here to allow/disallow hooking)
|
||||
1.2 Install the hook by overwriting the vector table exception handler with the hooked function
|
||||
2. Upon system crash (for example, by a dereference of a NULL pointer or anything else)
|
||||
2.1 The CPU triggers its exception and jump into the vector table for the exception type
|
||||
2.2 Instead of finding the default handler, it finds the updated pointer to our hook
|
||||
2.3 The CPU jumps into our hook function (likely a naked function to keep all information about crash point intact)
|
||||
2.4 The hook (naked) function saves the important registers (stack pointer, program counter, current mode) and jumps to a common exception handler (in C)
|
||||
2.5 The common exception handler dumps the registers on the serial link and perform a backtrace around the crashing point
|
||||
2.6 Once the backtrace is performed the last resort function is called (platform specific).
|
||||
On some platform with a LCD screen, this might display the crash information as a QR code or as text for the
|
||||
user to capture by taking a picture
|
||||
2.7 The CPU is reset and/or halted by triggering a debug breakpoint if a debugger is attached */
|
||||
|
||||
// Hook into CPU exception interrupt table to call the backtracing code upon an exception
|
||||
// Most platform will simply do nothing here, but those who can will install/overwrite the default exception handler
|
||||
// with a more performant exception handler
|
||||
void hook_cpu_exceptions();
|
||||
|
||||
// Some platform might deal without a hard fault handler, in that case, return 0 in your platform here or skip implementing it
|
||||
void * __attribute__((weak)) hook_get_hardfault_vector_address(unsigned base_address);
|
||||
// Some platform might deal without a memory management fault handler, in that case, return 0 in your platform here or skip implementing it
|
||||
void * __attribute__((weak)) hook_get_memfault_vector_address(unsigned base_address);
|
||||
// Some platform might deal without a bus fault handler, in that case, return 0 in your platform here or skip implementing it
|
||||
void * __attribute__((weak)) hook_get_busfault_vector_address(unsigned base_address);
|
||||
// Some platform might deal without a usage fault handler, in that case, return 0 in your platform here or skip implementing it
|
||||
void * __attribute__((weak)) hook_get_usagefault_vector_address(unsigned base_address);
|
||||
|
||||
// Last resort function that can be called after the exception handler was called.
|
||||
void __attribute__((weak)) hook_last_resort_func();
|
2
Marlin/src/HAL/shared/eeprom_api.cpp
Executable file → Normal file
2
Marlin/src/HAL/shared/eeprom_api.cpp
Executable file → Normal file
@@ -17,7 +17,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
32
Marlin/src/HAL/shared/eeprom_api.h
Executable file → Normal file
32
Marlin/src/HAL/shared/eeprom_api.h
Executable file → Normal file
@@ -17,7 +17,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -29,21 +29,39 @@
|
||||
|
||||
class PersistentStore {
|
||||
public:
|
||||
static bool access_start();
|
||||
static bool access_finish();
|
||||
static bool write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc);
|
||||
static bool read_data(int &pos, uint8_t* value, size_t size, uint16_t *crc, const bool writing=true);
|
||||
|
||||
// Total available persistent storage space (in bytes)
|
||||
static size_t capacity();
|
||||
|
||||
static inline bool write_data(const int pos, const uint8_t* value, const size_t size=sizeof(uint8_t)) {
|
||||
// Prepare to read or write
|
||||
static bool access_start();
|
||||
|
||||
// Housecleaning after read or write
|
||||
static bool access_finish();
|
||||
|
||||
// Write one or more bytes of data and update the CRC
|
||||
// Return 'true' on write error
|
||||
static bool write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc);
|
||||
|
||||
// Read one or more bytes of data and update the CRC
|
||||
// Return 'true' on read error
|
||||
static bool read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing=true);
|
||||
|
||||
// Write one or more bytes of data
|
||||
// Return 'true' on write error
|
||||
static inline bool write_data(const int pos, const uint8_t *value, const size_t size=sizeof(uint8_t)) {
|
||||
int data_pos = pos;
|
||||
uint16_t crc = 0;
|
||||
return write_data(data_pos, value, size, &crc);
|
||||
}
|
||||
|
||||
// Write a single byte of data
|
||||
// Return 'true' on write error
|
||||
static inline bool write_data(const int pos, const uint8_t value) { return write_data(pos, &value); }
|
||||
|
||||
static inline bool read_data(const int pos, uint8_t* value, const size_t size=1) {
|
||||
// Read one or more bytes of data
|
||||
// Return 'true' on read error
|
||||
static inline bool read_data(const int pos, uint8_t *value, const size_t size=1) {
|
||||
int data_pos = pos;
|
||||
uint16_t crc = 0;
|
||||
return read_data(data_pos, value, size, &crc);
|
||||
|
@@ -1,123 +0,0 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Description: functions for I2C connected external EEPROM.
|
||||
* Not platform dependent.
|
||||
*
|
||||
* TODO: Some platform Arduino libraries define these functions
|
||||
* so Marlin needs to add a glue layer to prevent the conflict.
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(I2C_EEPROM)
|
||||
|
||||
#include "../HAL.h"
|
||||
#include <Wire.h>
|
||||
|
||||
// ------------------------
|
||||
// Private Variables
|
||||
// ------------------------
|
||||
|
||||
static uint8_t eeprom_device_address = 0x50;
|
||||
|
||||
// ------------------------
|
||||
// Public functions
|
||||
// ------------------------
|
||||
|
||||
static void eeprom_init() {
|
||||
Wire.begin();
|
||||
}
|
||||
|
||||
void eeprom_write_byte(uint8_t *pos, unsigned char value) {
|
||||
unsigned eeprom_address = (unsigned) pos;
|
||||
|
||||
eeprom_init();
|
||||
|
||||
Wire.beginTransmission(I2C_ADDRESS(eeprom_device_address));
|
||||
Wire.write((int)(eeprom_address >> 8)); // MSB
|
||||
Wire.write((int)(eeprom_address & 0xFF)); // LSB
|
||||
Wire.write(value);
|
||||
Wire.endTransmission();
|
||||
|
||||
// wait for write cycle to complete
|
||||
// this could be done more efficiently with "acknowledge polling"
|
||||
delay(5);
|
||||
}
|
||||
|
||||
// WARNING: address is a page address, 6-bit end will wrap around
|
||||
// also, data can be maximum of about 30 bytes, because the Wire library has a buffer of 32 bytes
|
||||
void eeprom_update_block(const void *pos, void* eeprom_address, size_t n) {
|
||||
eeprom_init();
|
||||
|
||||
Wire.beginTransmission(I2C_ADDRESS(eeprom_device_address));
|
||||
Wire.write((int)((unsigned)eeprom_address >> 8)); // MSB
|
||||
Wire.write((int)((unsigned)eeprom_address & 0xFF)); // LSB
|
||||
Wire.endTransmission();
|
||||
|
||||
uint8_t *ptr = (uint8_t*)pos;
|
||||
uint8_t flag = 0;
|
||||
Wire.requestFrom(eeprom_device_address, (byte)n);
|
||||
for (byte c = 0; c < n && Wire.available(); c++)
|
||||
flag |= Wire.read() ^ ptr[c];
|
||||
|
||||
if (flag) {
|
||||
Wire.beginTransmission(I2C_ADDRESS(eeprom_device_address));
|
||||
Wire.write((int)((unsigned)eeprom_address >> 8)); // MSB
|
||||
Wire.write((int)((unsigned)eeprom_address & 0xFF)); // LSB
|
||||
Wire.write((uint8_t*)pos, n);
|
||||
Wire.endTransmission();
|
||||
|
||||
// wait for write cycle to complete
|
||||
// this could be done more efficiently with "acknowledge polling"
|
||||
delay(5);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t eeprom_read_byte(uint8_t *pos) {
|
||||
unsigned eeprom_address = (unsigned)pos;
|
||||
|
||||
eeprom_init();
|
||||
|
||||
Wire.beginTransmission(I2C_ADDRESS(eeprom_device_address));
|
||||
Wire.write((int)(eeprom_address >> 8)); // MSB
|
||||
Wire.write((int)(eeprom_address & 0xFF)); // LSB
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom(eeprom_device_address, (byte)1);
|
||||
return Wire.available() ? Wire.read() : 0xFF;
|
||||
}
|
||||
|
||||
// Don't read more than 30..32 bytes at a time!
|
||||
void eeprom_read_block(void* pos, const void* eeprom_address, size_t n) {
|
||||
eeprom_init();
|
||||
|
||||
Wire.beginTransmission(I2C_ADDRESS(eeprom_device_address));
|
||||
Wire.write((int)((unsigned)eeprom_address >> 8)); // MSB
|
||||
Wire.write((int)((unsigned)eeprom_address & 0xFF)); // LSB
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom(eeprom_device_address, (byte)n);
|
||||
for (byte c = 0; c < n; c++ )
|
||||
if (Wire.available()) *((uint8_t*)pos + c) = Wire.read();
|
||||
}
|
||||
|
||||
#endif // I2C_EEPROM
|
29
Marlin/src/HAL/shared/eeprom_if.h
Normal file
29
Marlin/src/HAL/shared/eeprom_if.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
*
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
* Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
|
||||
* Copyright (c) 2015-2016 Nico Tonnhofer wurstnase.reprap@gmail.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// EEPROM
|
||||
//
|
||||
void eeprom_init();
|
||||
void eeprom_write_byte(uint8_t *pos, uint8_t value);
|
||||
uint8_t eeprom_read_byte(uint8_t *pos);
|
102
Marlin/src/HAL/shared/eeprom_if_i2c.cpp
Normal file
102
Marlin/src/HAL/shared/eeprom_if_i2c.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Platform-independent Arduino functions for I2C EEPROM.
|
||||
* Enable USE_SHARED_EEPROM if not supplied by the framework.
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(I2C_EEPROM)
|
||||
|
||||
#include "eeprom_if.h"
|
||||
|
||||
#if ENABLED(SOFT_I2C_EEPROM)
|
||||
#include <SlowSoftWire.h>
|
||||
SlowSoftWire Wire = SlowSoftWire(I2C_SDA_PIN, I2C_SCL_PIN, true);
|
||||
#else
|
||||
#include <Wire.h>
|
||||
#endif
|
||||
|
||||
void eeprom_init() {
|
||||
Wire.begin(
|
||||
#if PINS_EXIST(I2C_SCL, I2C_SDA) && DISABLED(SOFT_I2C_EEPROM)
|
||||
uint8_t(I2C_SDA_PIN), uint8_t(I2C_SCL_PIN)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
#if ENABLED(USE_SHARED_EEPROM)
|
||||
|
||||
#ifndef EEPROM_WRITE_DELAY
|
||||
#define EEPROM_WRITE_DELAY 5
|
||||
#endif
|
||||
#ifndef EEPROM_DEVICE_ADDRESS
|
||||
#define EEPROM_DEVICE_ADDRESS 0x50
|
||||
#endif
|
||||
|
||||
static constexpr uint8_t eeprom_device_address = I2C_ADDRESS(EEPROM_DEVICE_ADDRESS);
|
||||
|
||||
// ------------------------
|
||||
// Public functions
|
||||
// ------------------------
|
||||
|
||||
#define SMALL_EEPROM (MARLIN_EEPROM_SIZE <= 2048)
|
||||
|
||||
// Combine Address high bits into the device address on <=16Kbit (2K) and >512Kbit (64K) EEPROMs.
|
||||
// Note: MARLIN_EEPROM_SIZE is specified in bytes, whereas EEPROM model numbers refer to bits.
|
||||
// e.g., The "16" in BL24C16 indicates a 16Kbit (2KB) size.
|
||||
static uint8_t _eeprom_calc_device_address(uint8_t * const pos) {
|
||||
const unsigned eeprom_address = (unsigned)pos;
|
||||
return (SMALL_EEPROM || MARLIN_EEPROM_SIZE > 65536)
|
||||
? uint8_t(eeprom_device_address | ((eeprom_address >> (SMALL_EEPROM ? 8 : 16)) & 0x07))
|
||||
: eeprom_device_address;
|
||||
}
|
||||
|
||||
static void _eeprom_begin(uint8_t * const pos) {
|
||||
const unsigned eeprom_address = (unsigned)pos;
|
||||
Wire.beginTransmission(_eeprom_calc_device_address(pos));
|
||||
if (!SMALL_EEPROM)
|
||||
Wire.write(uint8_t((eeprom_address >> 8) & 0xFF)); // Address High, if needed
|
||||
Wire.write(uint8_t(eeprom_address & 0xFF)); // Address Low
|
||||
}
|
||||
|
||||
void eeprom_write_byte(uint8_t *pos, uint8_t value) {
|
||||
_eeprom_begin(pos);
|
||||
Wire.write(value);
|
||||
Wire.endTransmission();
|
||||
|
||||
// wait for write cycle to complete
|
||||
// this could be done more efficiently with "acknowledge polling"
|
||||
delay(EEPROM_WRITE_DELAY);
|
||||
}
|
||||
|
||||
uint8_t eeprom_read_byte(uint8_t *pos) {
|
||||
_eeprom_begin(pos);
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom(_eeprom_calc_device_address(pos), (byte)1);
|
||||
return Wire.available() ? Wire.read() : 0xFF;
|
||||
}
|
||||
|
||||
#endif // USE_SHARED_EEPROM
|
||||
#endif // I2C_EEPROM
|
84
Marlin/src/HAL/shared/eeprom_if_spi.cpp
Normal file
84
Marlin/src/HAL/shared/eeprom_if_spi.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Platform-independent Arduino functions for SPI EEPROM.
|
||||
* Enable USE_SHARED_EEPROM if not supplied by the framework.
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(SPI_EEPROM)
|
||||
|
||||
#include "eeprom_if.h"
|
||||
|
||||
void eeprom_init() {}
|
||||
|
||||
#if ENABLED(USE_SHARED_EEPROM)
|
||||
|
||||
#define CMD_WREN 6 // WREN
|
||||
#define CMD_READ 2 // WRITE
|
||||
#define CMD_WRITE 2 // WRITE
|
||||
|
||||
#ifndef EEPROM_WRITE_DELAY
|
||||
#define EEPROM_WRITE_DELAY 7
|
||||
#endif
|
||||
|
||||
static void _eeprom_begin(uint8_t * const pos, const uint8_t cmd) {
|
||||
const uint8_t eeprom_temp[3] = {
|
||||
cmd,
|
||||
(unsigned(pos) >> 8) & 0xFF, // Address High
|
||||
unsigned(pos) & 0xFF // Address Low
|
||||
};
|
||||
WRITE(SPI_EEPROM1_CS_PIN, HIGH); // Usually free already
|
||||
WRITE(SPI_EEPROM1_CS_PIN, LOW); // Activate the Bus
|
||||
spiSend(SPI_CHAN_EEPROM1, eeprom_temp, 3);
|
||||
// Leave the Bus in-use
|
||||
}
|
||||
|
||||
uint8_t eeprom_read_byte(uint8_t *pos) {
|
||||
_eeprom_begin(pos, CMD_READ); // Set read location and begin transmission
|
||||
|
||||
const uint8_t v = spiRec(SPI_CHAN_EEPROM1); // After READ a value sits on the Bus
|
||||
|
||||
WRITE(SPI_EEPROM1_CS_PIN, HIGH); // Done with device
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void eeprom_write_byte(uint8_t *pos, uint8_t value) {
|
||||
const uint8_t eeprom_temp = CMD_WREN;
|
||||
WRITE(SPI_EEPROM1_CS_PIN, LOW);
|
||||
spiSend(SPI_CHAN_EEPROM1, &eeprom_temp, 1); // Write Enable
|
||||
|
||||
WRITE(SPI_EEPROM1_CS_PIN, HIGH); // Done with the Bus
|
||||
delay(1); // For a small amount of time
|
||||
|
||||
_eeprom_begin(pos, CMD_WRITE); // Set write address and begin transmission
|
||||
|
||||
spiSend(SPI_CHAN_EEPROM1, value); // Send the value to be written
|
||||
WRITE(SPI_EEPROM1_CS_PIN, HIGH); // Done with the Bus
|
||||
delay(EEPROM_WRITE_DELAY); // Give page write time to complete
|
||||
}
|
||||
|
||||
#endif // USE_SHARED_EEPROM
|
||||
#endif // I2C_EEPROM
|
@@ -1,118 +0,0 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Description: functions for SPI connected external EEPROM.
|
||||
* Not platform dependent.
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(SPI_EEPROM)
|
||||
|
||||
#include "../HAL.h"
|
||||
|
||||
#define CMD_WREN 6 // WREN
|
||||
#define CMD_READ 2 // WRITE
|
||||
#define CMD_WRITE 2 // WRITE
|
||||
|
||||
uint8_t eeprom_read_byte(uint8_t* pos) {
|
||||
uint8_t v;
|
||||
uint8_t eeprom_temp[3];
|
||||
|
||||
// set read location
|
||||
// begin transmission from device
|
||||
eeprom_temp[0] = CMD_READ;
|
||||
eeprom_temp[1] = ((unsigned)pos>>8) & 0xFF; // addr High
|
||||
eeprom_temp[2] = (unsigned)pos& 0xFF; // addr Low
|
||||
WRITE(SPI_EEPROM1_CS, HIGH);
|
||||
WRITE(SPI_EEPROM1_CS, LOW);
|
||||
spiSend(SPI_CHAN_EEPROM1, eeprom_temp, 3);
|
||||
|
||||
v = spiRec(SPI_CHAN_EEPROM1);
|
||||
WRITE(SPI_EEPROM1_CS, HIGH);
|
||||
return v;
|
||||
}
|
||||
|
||||
void eeprom_read_block(void* dest, const void* eeprom_address, size_t n) {
|
||||
uint8_t eeprom_temp[3];
|
||||
|
||||
// set read location
|
||||
// begin transmission from device
|
||||
eeprom_temp[0] = CMD_READ;
|
||||
eeprom_temp[1] = ((unsigned)eeprom_address>>8) & 0xFF; // addr High
|
||||
eeprom_temp[2] = (unsigned)eeprom_address& 0xFF; // addr Low
|
||||
WRITE(SPI_EEPROM1_CS, HIGH);
|
||||
WRITE(SPI_EEPROM1_CS, LOW);
|
||||
spiSend(SPI_CHAN_EEPROM1, eeprom_temp, 3);
|
||||
|
||||
uint8_t *p_dest = (uint8_t *)dest;
|
||||
while (n--)
|
||||
*p_dest++ = spiRec(SPI_CHAN_EEPROM1);
|
||||
WRITE(SPI_EEPROM1_CS, HIGH);
|
||||
}
|
||||
|
||||
void eeprom_write_byte(uint8_t* pos, uint8_t value) {
|
||||
uint8_t eeprom_temp[3];
|
||||
|
||||
/*write enable*/
|
||||
eeprom_temp[0] = CMD_WREN;
|
||||
WRITE(SPI_EEPROM1_CS, LOW);
|
||||
spiSend(SPI_CHAN_EEPROM1, eeprom_temp, 1);
|
||||
WRITE(SPI_EEPROM1_CS, HIGH);
|
||||
delay(1);
|
||||
|
||||
/*write addr*/
|
||||
eeprom_temp[0] = CMD_WRITE;
|
||||
eeprom_temp[1] = ((unsigned)pos>>8) & 0xFF; //addr High
|
||||
eeprom_temp[2] = (unsigned)pos & 0xFF; //addr Low
|
||||
WRITE(SPI_EEPROM1_CS, LOW);
|
||||
spiSend(SPI_CHAN_EEPROM1, eeprom_temp, 3);
|
||||
|
||||
spiSend(SPI_CHAN_EEPROM1, value);
|
||||
WRITE(SPI_EEPROM1_CS, HIGH);
|
||||
delay(7); // wait for page write to complete
|
||||
}
|
||||
|
||||
void eeprom_update_block(const void* src, void* eeprom_address, size_t n) {
|
||||
uint8_t eeprom_temp[3];
|
||||
|
||||
/*write enable*/
|
||||
eeprom_temp[0] = CMD_WREN;
|
||||
WRITE(SPI_EEPROM1_CS, LOW);
|
||||
spiSend(SPI_CHAN_EEPROM1, eeprom_temp, 1);
|
||||
WRITE(SPI_EEPROM1_CS, HIGH);
|
||||
delay(1);
|
||||
|
||||
/*write addr*/
|
||||
eeprom_temp[0] = CMD_WRITE;
|
||||
eeprom_temp[1] = ((unsigned)eeprom_address>>8) & 0xFF; //addr High
|
||||
eeprom_temp[2] = (unsigned)eeprom_address & 0xFF; //addr Low
|
||||
WRITE(SPI_EEPROM1_CS, LOW);
|
||||
spiSend(SPI_CHAN_EEPROM1, eeprom_temp, 3);
|
||||
|
||||
spiSend(SPI_CHAN_EEPROM1, (const uint8_t*)src, n);
|
||||
WRITE(SPI_EEPROM1_CS, HIGH);
|
||||
delay(7); // wait for page write to complete
|
||||
}
|
||||
|
||||
#endif // SPI_EEPROM
|
43
Marlin/src/HAL/shared/esp_wifi.cpp
Normal file
43
Marlin/src/HAL/shared/esp_wifi.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
#include "Delay.h"
|
||||
|
||||
void esp_wifi_init(void) { // init ESP01 WIFI module pins
|
||||
#if PIN_EXISTS(ESP_WIFI_MODULE_GPIO0)
|
||||
OUT_WRITE(ESP_WIFI_MODULE_GPIO0_PIN, HIGH);
|
||||
#endif
|
||||
#if PIN_EXISTS(ESP_WIFI_MODULE_GPIO2)
|
||||
OUT_WRITE(ESP_WIFI_MODULE_GPIO2_PIN, HIGH);
|
||||
#endif
|
||||
#if PIN_EXISTS(ESP_WIFI_MODULE_RESET)
|
||||
delay(1); // power up delay (0.1mS minimum)
|
||||
OUT_WRITE(ESP_WIFI_MODULE_RESET_PIN, LOW);
|
||||
delay(1);
|
||||
OUT_WRITE(ESP_WIFI_MODULE_RESET_PIN, HIGH);
|
||||
#endif
|
||||
#if PIN_EXISTS(ESP_WIFI_MODULE_ENABLE)
|
||||
delay(1); // delay after reset released (0.1mS minimum)
|
||||
OUT_WRITE(ESP_WIFI_MODULE_ENABLE_PIN, HIGH);
|
||||
#endif
|
||||
}
|
24
Marlin/src/HAL/shared/esp_wifi.h
Normal file
24
Marlin/src/HAL/shared/esp_wifi.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
void esp_wifi_init();
|
2
Marlin/src/HAL/shared/math_32bit.h
Executable file → Normal file
2
Marlin/src/HAL/shared/math_32bit.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
189
Marlin/src/HAL/shared/progmem.h
Normal file
189
Marlin/src/HAL/shared/progmem.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifndef __AVR__
|
||||
#ifndef __PGMSPACE_H_
|
||||
// This define should prevent reading the system pgmspace.h if included elsewhere
|
||||
// This is not normally needed.
|
||||
#define __PGMSPACE_H_ 1
|
||||
#endif
|
||||
|
||||
#ifndef PROGMEM
|
||||
#define PROGMEM
|
||||
#endif
|
||||
#ifndef PGM_P
|
||||
#define PGM_P const char *
|
||||
#endif
|
||||
#ifndef PSTR
|
||||
#define PSTR(str) (str)
|
||||
#endif
|
||||
#ifndef F
|
||||
#define F(str) (str)
|
||||
#endif
|
||||
#ifndef _SFR_BYTE
|
||||
#define _SFR_BYTE(n) (n)
|
||||
#endif
|
||||
#ifndef memchr_P
|
||||
#define memchr_P(str, c, len) memchr((str), (c), (len))
|
||||
#endif
|
||||
#ifndef memcmp_P
|
||||
#define memcmp_P(a, b, n) memcmp((a), (b), (n))
|
||||
#endif
|
||||
#ifndef memcpy_P
|
||||
#define memcpy_P(dest, src, num) memcpy((dest), (src), (num))
|
||||
#endif
|
||||
#ifndef memmem_P
|
||||
#define memmem_P(a, alen, b, blen) memmem((a), (alen), (b), (blen))
|
||||
#endif
|
||||
#ifndef memrchr_P
|
||||
#define memrchr_P(str, val, len) memrchr((str), (val), (len))
|
||||
#endif
|
||||
#ifndef strcat_P
|
||||
#define strcat_P(dest, src) strcat((dest), (src))
|
||||
#endif
|
||||
#ifndef strchr_P
|
||||
#define strchr_P(str, c) strchr((str), (c))
|
||||
#endif
|
||||
#ifndef strchrnul_P
|
||||
#define strchrnul_P(str, c) strchrnul((str), (c))
|
||||
#endif
|
||||
#ifndef strcmp_P
|
||||
#define strcmp_P(a, b) strcmp((a), (b))
|
||||
#endif
|
||||
#ifndef strcpy_P
|
||||
#define strcpy_P(dest, src) strcpy((dest), (src))
|
||||
#endif
|
||||
#ifndef strcasecmp_P
|
||||
#define strcasecmp_P(a, b) strcasecmp((a), (b))
|
||||
#endif
|
||||
#ifndef strcasestr_P
|
||||
#define strcasestr_P(a, b) strcasestr((a), (b))
|
||||
#endif
|
||||
#ifndef strlcat_P
|
||||
#define strlcat_P(dest, src, len) strlcat((dest), (src), (len))
|
||||
#endif
|
||||
#ifndef strlcpy_P
|
||||
#define strlcpy_P(dest, src, len) strlcpy((dest), (src), (len))
|
||||
#endif
|
||||
#ifndef strlen_P
|
||||
#define strlen_P(s) strlen((const char *)(s))
|
||||
#endif
|
||||
#ifndef strnlen_P
|
||||
#define strnlen_P(str, len) strnlen((str), (len))
|
||||
#endif
|
||||
#ifndef strncmp_P
|
||||
#define strncmp_P(a, b, n) strncmp((a), (b), (n))
|
||||
#endif
|
||||
#ifndef strncasecmp_P
|
||||
#define strncasecmp_P(a, b, n) strncasecmp((a), (b), (n))
|
||||
#endif
|
||||
#ifndef strncat_P
|
||||
#define strncat_P(a, b, n) strncat((a), (b), (n))
|
||||
#endif
|
||||
#ifndef strncpy_P
|
||||
#define strncpy_P(a, b, n) strncpy((a), (b), (n))
|
||||
#endif
|
||||
#ifndef strpbrk_P
|
||||
#define strpbrk_P(str, chrs) strpbrk((str), (chrs))
|
||||
#endif
|
||||
#ifndef strrchr_P
|
||||
#define strrchr_P(str, c) strrchr((str), (c))
|
||||
#endif
|
||||
#ifndef strsep_P
|
||||
#define strsep_P(strp, delim) strsep((strp), (delim))
|
||||
#endif
|
||||
#ifndef strspn_P
|
||||
#define strspn_P(str, chrs) strspn((str), (chrs))
|
||||
#endif
|
||||
#ifndef strstr_P
|
||||
#define strstr_P(a, b) strstr((a), (b))
|
||||
#endif
|
||||
#ifndef sprintf_P
|
||||
#define sprintf_P(s, ...) sprintf((s), __VA_ARGS__)
|
||||
#endif
|
||||
#ifndef vfprintf_P
|
||||
#define vfprintf_P(s, ...) vfprintf((s), __VA_ARGS__)
|
||||
#endif
|
||||
#ifndef printf_P
|
||||
#define printf_P(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef snprintf_P
|
||||
#define snprintf_P(s, n, ...) snprintf((s), (n), __VA_ARGS__)
|
||||
#endif
|
||||
#ifndef vsprintf_P
|
||||
#define vsprintf_P(s, ...) vsprintf((s),__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef vsnprintf_P
|
||||
#define vsnprintf_P(s, n, ...) vsnprintf((s), (n),__VA_ARGS__)
|
||||
#endif
|
||||
#ifndef fprintf_P
|
||||
#define fprintf_P(s, ...) fprintf((s), __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef pgm_read_byte
|
||||
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
|
||||
#endif
|
||||
#ifndef pgm_read_word
|
||||
#define pgm_read_word(addr) (*(const unsigned short *)(addr))
|
||||
#endif
|
||||
#ifndef pgm_read_dword
|
||||
#define pgm_read_dword(addr) (*(const unsigned long *)(addr))
|
||||
#endif
|
||||
#ifndef pgm_read_float
|
||||
#define pgm_read_float(addr) (*(const float *)(addr))
|
||||
#endif
|
||||
|
||||
#ifndef pgm_read_byte_near
|
||||
#define pgm_read_byte_near(addr) pgm_read_byte(addr)
|
||||
#endif
|
||||
#ifndef pgm_read_word_near
|
||||
#define pgm_read_word_near(addr) pgm_read_word(addr)
|
||||
#endif
|
||||
#ifndef pgm_read_dword_near
|
||||
#define pgm_read_dword_near(addr) pgm_read_dword(addr)
|
||||
#endif
|
||||
#ifndef pgm_read_float_near
|
||||
#define pgm_read_float_near(addr) pgm_read_float(addr)
|
||||
#endif
|
||||
#ifndef pgm_read_byte_far
|
||||
#define pgm_read_byte_far(addr) pgm_read_byte(addr)
|
||||
#endif
|
||||
#ifndef pgm_read_word_far
|
||||
#define pgm_read_word_far(addr) pgm_read_word(addr)
|
||||
#endif
|
||||
#ifndef pgm_read_dword_far
|
||||
#define pgm_read_dword_far(addr) pgm_read_dword(addr)
|
||||
#endif
|
||||
#ifndef pgm_read_float_far
|
||||
#define pgm_read_float_far(addr) pgm_read_float(addr)
|
||||
#endif
|
||||
|
||||
#ifndef pgm_read_pointer
|
||||
#define pgm_read_pointer
|
||||
#endif
|
||||
|
||||
// Fix bug in pgm_read_ptr
|
||||
#undef pgm_read_ptr
|
||||
#define pgm_read_ptr(addr) (*((void**)(addr)))
|
||||
|
||||
#endif // __AVR__
|
7
Marlin/src/HAL/shared/servo.cpp
Executable file → Normal file
7
Marlin/src/HAL/shared/servo.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
* readMicroseconds() - Get the last-written servo pulse width in microseconds.
|
||||
* attached() - Return true if a servo is attached.
|
||||
* detach() - Stop an attached servo from pulsing its i/o pin.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
@@ -150,9 +149,7 @@ void Servo::move(const int value) {
|
||||
if (attach(0) >= 0) {
|
||||
write(value);
|
||||
safe_delay(servo_delay[servoIndex]);
|
||||
#if ENABLED(DEACTIVATE_SERVOS_AFTER_MOVE)
|
||||
detach();
|
||||
#endif
|
||||
TERN_(DEACTIVATE_SERVOS_AFTER_MOVE, detach());
|
||||
}
|
||||
}
|
||||
|
||||
|
7
Marlin/src/HAL/shared/servo.h
Executable file → Normal file
7
Marlin/src/HAL/shared/servo.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -41,7 +41,6 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method.
|
||||
* The servos are pulsed in the background using the value most recently written using the write() method
|
||||
*
|
||||
@@ -71,12 +70,12 @@
|
||||
#include "../TEENSY31_32/Servo.h"
|
||||
#elif IS_TEENSY35 || IS_TEENSY36
|
||||
#include "../TEENSY35_36/Servo.h"
|
||||
#elif IS_TEENSY40 || IS_TEENSY41
|
||||
#include "../TEENSY40_41/Servo.h"
|
||||
#elif defined(TARGET_LPC1768)
|
||||
#include "../LPC1768/Servo.h"
|
||||
#elif defined(__STM32F1__) || defined(TARGET_STM32F1)
|
||||
#include "../STM32F1/Servo.h"
|
||||
#elif defined(STM32GENERIC) && defined(STM32F4)
|
||||
#include "../STM32_F4_F7/Servo.h"
|
||||
#elif defined(ARDUINO_ARCH_STM32)
|
||||
#include "../STM32/Servo.h"
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
|
2
Marlin/src/HAL/shared/servo_private.h
Executable file → Normal file
2
Marlin/src/HAL/shared/servo_private.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
Reference in New Issue
Block a user