software_dfu #46

Merged
sb10q merged 8 commits from software_dfu into master 2021-01-13 11:59:06 +08:00
5 changed files with 68 additions and 3 deletions

View File

@ -124,6 +124,7 @@ formatted as line-delimited JSON.
| `load [0/1]` | Restore configuration for channel all/0/1 from flash |
| `save [0/1]` | Save configuration for channel all/0/1 to flash |
| `reset` | Reset the device |
| `dfu` | Reset device and enters USB device firmware update (DFU) mode |
Outdated
Review

Table formatting does not seem OK.

Table formatting does not seem OK.
| `ipv4 <X.X.X.X/L> [Y.Y.Y.Y]` | Configure IPv4 address, netmask length, and optional default gateway |

View File

@ -3,10 +3,13 @@ MEMORY
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
/* reserved for config data */
CONFIG (rx) : ORIGIN = 0x8100000, LENGTH = 16K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 112K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 112K - 4
/* reserved for DFU trigger message */
DFU_MSG (wrx) : ORIGIN = 0x2001BFFC, LENGTH = 4
Outdated
Review

The length should be 4 and not 1K AFAICT?

The length should be 4 and not 1K AFAICT?
RAM2 (xrw) : ORIGIN = 0x2001C000, LENGTH = 16K
RAM3 (xrw) : ORIGIN = 0x20020000, LENGTH = 64K
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
}
_stack_start = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
_dfu_msg = ORIGIN(DFU_MSG);

View File

@ -179,6 +179,7 @@ pub enum Command {
channel: usize,
rate: Option<f32>,
},
Dfu,
}
fn end(input: &[u8]) -> IResult<&[u8], ()> {
@ -535,6 +536,7 @@ fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
pid,
steinhart_hart,
postfilter,
value(Ok(Command::Dfu), tag("dfu")),
))(input)
}

49
src/dfu.rs Normal file
View File

@ -0,0 +1,49 @@
use cortex_m_rt::{pre_init};
const DFU_TRIG_MSG: u32 = 0xDECAFBAD;
Outdated
Review
This can and should be automatically obtained via a linker symbol. See this example: https://git.m-labs.hk/M-Labs/artiq-zynq/src/branch/master/src/runtime/src/kernel/core1.rs#L31-L36 https://git.m-labs.hk/M-Labs/artiq-zynq/src/branch/master/src/runtime/link.x#L10

The linker currently does not actually do anything to the ram carved out for the dfu trigger message, and I beleive there's no symbol generated for that chunk of memory.

I think I'll have to find the linker script the project is using, fork a copy, generate a symbol for that addr and use it in our project somehow.

The linker currently does not actually do anything to the ram carved out for the dfu trigger message, and I beleive there's no symbol generated for that chunk of memory. I think I'll have to find the linker script the project is using, fork a copy, generate a symbol for that addr and use it in our project somehow.
Outdated
Review

Indeed, but the linker script can be modified so that such a symbol is generated. This way there isn't any value that needs to be manually synchronized in two places. You may not even need to hardcode the address; the linker could place that variable automatically.

Indeed, but the linker script can be modified so that such a symbol is generated. This way there isn't any value that needs to be manually synchronized in two places. You may not even need to hardcode the address; the linker could place that variable automatically.
Outdated
Review

Though hardcoding is actually more reliable since after a firmware update the address could have changed otherwise... add a comment that explains that.

Though hardcoding is actually more reliable since after a firmware update the address could have changed otherwise... add a comment that explains that.
Outdated
Review

The linker script is the second link I gave you.

The linker script is the second link I gave you.

I think specifying a region that only contains the DFU msg would be reasonable. This way the address would not be changed after update.

I think specifying a region that only contains the DFU msg would be reasonable. This way the address would not be changed after update.

I thought that linker script is for a zynq? I am not sure where the linker script currently being used is placed, think it is packaged with one of the rust modules.

I thought that linker script is for a zynq? I am not sure where the linker script currently being used is placed, think it is packaged with one of the rust modules.
Outdated
Review

Okay, you mean the thermostat linker script.
That memory.x you modified can be used, as you can see it already defines the _stack_start symbol.

Okay, you mean the thermostat linker script. That ``memory.x`` you modified can be used, as you can see it already defines the ``_stack_start`` symbol.

The stack is on CCM, the message is placed in RAM. I can try placing the msg in CCM, not sure what the reset behavior of CCM is yet.

The stack is on CCM, the message is placed in RAM. I can try placing the msg in CCM, not sure what the reset behavior of CCM is yet.

@pca006132 There's already a dedicated region specified in memory.x . Though the linker does not do anything with that region, and I don't think the linker generates a symbol for that space.

I also haven't found the linker script of the project just yet.

@pca006132 There's already a dedicated region specified in memory.x . Though the linker does not do anything with that region, and I don't think the linker generates a symbol for that space. I also haven't found the linker script of the project just yet.

The linker script is defined in cortex-m-rt, https://github.com/rust-embedded/cortex-m-rt/blob/master/link.x.in
But I'm not familiar with that, not sure how to add new symbols.

The linker script is defined in `cortex-m-rt`, https://github.com/rust-embedded/cortex-m-rt/blob/master/link.x.in But I'm not familiar with that, not sure how to add new symbols.
Outdated
Review

@topquark12 what's wrong with simply adding

_dfu_msg = ORIGIN(DFU_MSG)

at the end of thermostat's memory.x?

@topquark12 what's wrong with simply adding ``` _dfu_msg = ORIGIN(DFU_MSG) ``` at the end of thermostat's ``memory.x``?
extern "C" {
// This symbol comes from memory.x
Outdated
Review

Why not put it outside the functions so this declaration can be shared?

Why not put it outside the functions so this declaration can be shared?
static mut _dfu_msg: u32;
}
pub unsafe fn set_dfu_trigger() {
_dfu_msg = DFU_TRIG_MSG;
}
/// Called by reset handler in lib.rs immediately after reset.
/// This function should not be called outside of reset handler as
/// bootloader expects MCU to be in reset state when called.
#[pre_init]
Outdated
Review

see comment above

see comment above
unsafe fn __pre_init() {
Outdated
Review

remove blank line

remove blank line
if _dfu_msg == DFU_TRIG_MSG {
_dfu_msg = 0x00000000;
// Enable system config controller clock
Outdated
Review

space before {

space before {
const RCC_APB2ENR: *mut u32 = 0xE000_ED88 as *mut u32;
Review

remove blank line

remove blank line
const RCC_APB2ENR_ENABLE_SYSCFG_CLOCK: u32 = 0x00004000;
core::ptr::write_volatile(
RCC_APB2ENR,
*RCC_APB2ENR | RCC_APB2ENR_ENABLE_SYSCFG_CLOCK,
);
// Bypass BOOT pins and remap bootloader to 0x00000000
const SYSCFG_MEMRMP: *mut u32 = 0x40013800 as *mut u32;
const SYSCFG_MEMRMP_MAP_ROM: u32 = 0x00000001;
core::ptr::write_volatile(
SYSCFG_MEMRMP,
*SYSCFG_MEMRMP | SYSCFG_MEMRMP_MAP_ROM,
);
asm!(
// Set stack pointer to bootloader location
"LDR R0, =0x1FFF0000",
"LDR SP,[R0, #0]",
// Jump to bootloader
"LDR R0,[R0, #4]",
"BX R0",
);
}
}

View File

@ -1,6 +1,6 @@
#![cfg_attr(not(test), no_std)]
#![cfg_attr(not(test), no_main)]
#![feature(maybe_uninit_extra, maybe_uninit_ref)]
#![feature(maybe_uninit_extra, maybe_uninit_ref, asm)]
#![cfg_attr(test, allow(unused))]
// TODO: #![deny(warnings, unused)]
@ -66,6 +66,7 @@ mod channel_state;
mod config;
use config::ChannelConfig;
mod flash_store;
mod dfu;
const HSE: MegaHertz = MegaHertz(8);
#[cfg(not(feature = "semihosting"))]
@ -77,7 +78,6 @@ const CHANNEL_CONFIG_KEY: [&str; 2] = ["ch0", "ch1"];
const TCP_PORT: u16 = 23;
fn send_line(socket: &mut TcpSocket, data: &[u8]) -> bool {
let send_free = socket.send_capacity() - socket.send_queue();
if data.len() > send_free + 1 {
@ -422,6 +422,16 @@ fn main() -> ! {
channels.power_down(i);
}
SCB::sys_reset();
}
Command::Dfu => {
for i in 0..CHANNELS {
channels.power_down(i);
}
unsafe {
dfu::set_dfu_trigger();
}
Outdated
Review

Does this work or do we need to wait for the network driver/hardware to actually send the packet?
What if there are other open sockets? Maybe address this entire issue in a second PR.

Does this work or do we need to wait for the network driver/hardware to actually send the packet? What if there are other open sockets? Maybe address this entire issue in a second PR.

was just testing out the behavior of that, forgot to remove before committing.

was just testing out the behavior of that, forgot to remove before committing.
SCB::sys_reset();
}
}