<chrysn[m]>
<KevinPFleming[m]> "Or in my case, it's read_exact..." <- Are you working in an async context? If so, you could do a plain read_exact racing against an idle timeout. (Assuming that the length comes from some initial byte you read w/o a timeout).
thejpster[m] has joined #rust-embedded
<thejpster[m]>
How do we feel about the #[entry] macro in cortex-m-rt? Is that the best way to mark a function as the one to be called at the end of the init routine? Any other syntax or mechanisms I could consider instead?
diondokter[m] has joined #rust-embedded
<diondokter[m]>
thejpster[m]: Could do it by name. The RT crate could define an extern `main` function and call that.
<diondokter[m]>
Not sure what other options there are
<thejpster[m]>
Looking at riscv-rt it does something similar. The function gets a special __ name so it doesn’t collide with any other function in the file, but the function is given the exported symbol main, which is what the assembly branches to.
<thejpster[m]>
Actually this leads me to a question. Is it sound to jump from assembly to an extern “Rust” function from assembly.
<thejpster[m]>
s/from assembly//
<diondokter[m]>
thejpster[m]: Hmmm... Good question!
<diondokter[m]>
Should probably asm -> extern C -> rust
<thejpster[m]>
The Rust ABI could at some point be changed to require the caller to adjust SP before the call.
<diondokter[m]>
* Should probably be asm ->
<thejpster[m]>
Like how some Windows ABIs work (I dimly recall).
<thejpster[m]>
Yes I think there should be an extern C trampoline which calls a Rust function with a well known name. Unclear if the entry macro should make that function or if it should exist in the RT library.
<thejpster[m]>
Also, this implies all the Cortex-M interrupt and exception handlers should be extern “C” because the hardware assumes a certain calling convention.
<diondokter[m]>
But really, the next (or the one after) we'll have naked functions stabilized which is probably what should be used for any preinit function
<diondokter[m]>
Ok, the non-trampoline hardfault doesn't use extern C
<thejpster[m]>
OK I’m going to add entry and exception macros to cortex-r-rt and cortex-a-rt and I’ll make sure asm code only ever jumps to extern C code.
<KevinPFleming[m]>
<chrysn[m]> "Are you working in an async..." <- I am, but as you noted I'd have to use something else to read the first byte (or use `read_ready` as I'm doing now). I'll consider that option too.
<thejpster[m]>
Then I’d like to do a release. I have a big demo to do on Wednesday.
<thejpster[m]>
Writing proc_macros is not fun at all. I struggled to get rust-analyser to do anything useful for me, I don't know the APIs I need to get the information I want from the input to the macro, and the error messages were unhelpful.
<thejpster[m]>
Hopefully it was worth the pain.
<thejpster[m]>
I know we talked at length at RustNL last year about #[exception] fn SysTick() {} vs #[exception(SysTick) fn do_stuff() { } - cortex-m-rt does the former but I prefer the latter.
pcs38 has quit [Ping timeout: 252 seconds]
pcs38 has joined #rust-embedded
<thejpster[m]>
oh no. I added a test for re-entrant interrupts and it's failing. I guess my interrupt handler isn't bullet-proof. I'm tripping over a debug assert in the semihosting crate.
<thejpster[m]>
Hurray for tests I guess?
<thejpster[m]>
does anyone know how to make GDB print the banked registers? I can see CPSR and LR but I want to see SPSR_IRQ and LR_IRQ.
<jason-kairos[m]>
in my experience that's the debug server's responsibility to handle
<jason-kairos[m]>
at least on embedded. Edit: I guess this is aarch64, in which case it is outside of my depth
<thejpster[m]>
The debug server is QEMU, and this is AArch32
sroemer has quit [Quit: WeeChat 4.5.2]
<thejpster[m]>
I seem to have compiled QEMU 9 without GTK support, so I have no console. Let me fix that.
<thejpster[m]>
right, we're at "compile a custom version of QEMU" levels of failure at the moment
<thejpster[m]>
but this custom version of QEMU can not only dump the registers for the current mode, but all the banked registers as well
<thejpster[m]>
well crap, I'm corrupting LR somehow
<RobinMueller[m]>
I managed to make defmt work via UART on an MSP430. amazing :D Unfortunately, we only have one UART for all our communication, and we want to exchange space packets (packet protocol). How would I approach wrapping the encoded frames with yet another packet protocol? I guess I also need my client app to unpack the defmt frames and then pass them to defmt-print?
<RobinMueller[m]>
* I managed to make defmt work via UART on an MSP430. amazing :D Unfortunately, we only have one UART for all our communication, and we want to exchange space packets (packet protocol). How would I approach wrapping the encoded frames with yet another packet protocol? I guess I also need my client app to unpack the defmt frames and then pass them to defmt-print?
<RobinMueller[m]>
* need my python client app, * client app which does the UART communication to unpack
<JamesMunns[m]>
You could use postcard-rpc as a wire protocol, it lets you multiplex different frames over one link. There might not be ready to use building blocks for the msp430, especially if you're not already doing async there.
<JamesMunns[m]>
But yeah, you need SOME kind of method of framing and multiplexing, either inside your existing protocol, or using some other protocol to encapsulate the different kind of frames.
<thejpster[m]>
sweet, I fixed it. I don't precisely know *why* it fixes it, but it makes sense if I don't think too hard.
<thejpster[m]>
(I need to save LR to the stack and pop LR off the stack once the C handler is complete; and I need to run the C handler in a mode that isn't IRQ mode, because it won't save CPSR to SPSR_irq and LR to LR_irq if you get an IRQ and you're already in IRQ mode).
<RobinMueller[m]>
it's just a style thing to be honest. naming the macro interrupt or interrupt_handler would be a bit more idiomatic and similar to how other crates like cortex-m do it..
<RobinMueller[m]>
* it's just a style thing to be honest. naming the macro interrupt or interrupt_handler would be a bit more idiomatic in my opinion, and more similar to how other crates like cortex-m do it..
<RobinMueller[m]>
* it's just a style thing to be honest. naming the macro interrupt or interrupt_handler would be a bit more idiomatic in my opinion, and more similar to how other crates like cortex-m do it.. But maybe having it like this also makes it more clear that the interrupt handler is just an exception type like the other exceptions
<RobinMueller[m]>
* it's just a style thing to be honest. naming the macro interrupt or interrupt_handler would be a bit more idiomatic in my opinion, and more similar to how other crates like cortex-m do it.. But maybe having it like this also makes it more clear that the interrupt handler is just an exception type like the other exceptions. maybe it is also possible that both are available and both have the same code-generation function,
<RobinMueller[m]>
but this might just make it more confusing
<RobinMueller[m]>
s//but putting it into a packet/frame would require additional buffers again.. I guess there is no way around that?/
pcs38 has quit [Ping timeout: 276 seconds]
<JamesMunns[m]>
It depends on how you manage your interface. defmt is *immediate*, it writes to it's encoder. That means if you are multiplexing, you often need to buffer it, then have your interface multiplex the multiple "sources" into a single "sink"
<JamesMunns[m]>
so, I'd probably say "almost always yes, you will want to buffer it somewhere", that's what defmt-rtt does, and what defmt-bbq does
<JamesMunns[m]>
The alternative imo would be to have some kind of critical section that prevents defmt data from being emitted while you are sending something else on the interface
<JamesMunns[m]>
but that's not-great for other reasons :D
<RobinMueller[m]>
hmm. my plan with the space packets was to use COBS as a framing layer , and exchange COBS encoded packets
<JamesMunns[m]>
I'm not familiar with space packets, but framing is just one part of multiplexing, the other part is de-multiplexing: how does the other side know if it was sent a "space packets" frame or a "defmt data" frame
<JamesMunns[m]>
so you could "just" add a header at the start of the message, "1" for space packets, "2" for defmt, etc
<RobinMueller[m]>
the space packet has an APID ID field
<RobinMueller[m]>
i just use one APID for the regular COM protocol, and one other specific for defmt frame
<JamesMunns[m]>
gotcha! I don't know what those letters mean, but if it already supports multiplexing as part of the protocol, that seems reasonable!
<RobinMueller[m]>
s/frame/frames.. at least that was my idea in theory/
<RobinMueller[m]>
application process identifier
<RobinMueller[m]>
just a 14-bit ID field
<RobinMueller[m]>
one question is: how do I know what the maximum data buffer passed inside these encoder closures are? because if I need to encode everything with COBS, I need to be able to buffer that maximum data size
<JamesMunns[m]>
well, cobs can be done with a fixed (256 byte?) buffering window
<RobinMueller[m]>
maximum data size + space packet overhead + COBS overhead
<JamesMunns[m]>
you only need to "seek back" far enough to fixup the offset packets
<JamesMunns[m]>
there's also rcobs/rzcobs that defmt also uses that doesn't require seek-back buffering
<JamesMunns[m]>
(but makes decoding a bit harder, which is fine if the other system is a PC)
<RobinMueller[m]>
buffering window? would that mean splitting the packets again? The way I used COBS was also as a simple framing layer. I just encoded the whole packet, delimited everything with the sentinel, and sent it at once
<RobinMueller[m]>
ah wait
<JamesMunns[m]>
yes, you can do it that way, you could also buffer as a stream, one chunk at a time
<RobinMueller[m]>
ahhh now I get it.. use a streaming encoder
<JamesMunns[m]>
once you hit a data-zero (or the max stride size, which is like 254 bytes or something), you "flush" the data, then start encoding the next chunk
<JamesMunns[m]>
but that requires the defmt encoder having the ability to do that flushing
<JamesMunns[m]>
which it can't necessarily do
<JamesMunns[m]>
unless it has exclusive access to the serial port, e.g. you do the critical section thing, where defmt is inhibited while you are sending some non-defmt frame on the interface
<JamesMunns[m]>
or, like you said, you stick all the defmt data into a queue, then at the interface, you grab a chunk of data from defmt when you can, and stick it in a frame
<JamesMunns[m]>
your interface doesn't necessarily need to segment frames exactly where defmt does
<JamesMunns[m]>
you could send one 256 byte frame that contains 16 smaller defmt messages, or even crosses the boundaries of defmt messages
<JamesMunns[m]>
it's up to how you write your "stream re-assembler" on the other side.