-
Notifications
You must be signed in to change notification settings - Fork 99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MSP430 support #20
Comments
rust-lang/rust#37672 has a custom target specification in the PR description but To do that, we need to know low level details about the microcontrollers:
And about the tooling, in particular linker scripts. msp430-gcc ships with several linker scripts. Using the linker scripts
I expect this program should "work" if flashed into the micro. As least it Changing the C source code to: int main() {
while (1) {}
} Changes the disassembly like this: f818: 92 42 00 02 mov &0x0200,&0x0120
f81c: 20 01
f81e: 2f 83 decd r15
- f820: 9f 4f 4a f8 mov -1974(r15),512(r15);0xf84a(r15), 0x0200(r15)
+ f820: 9f 4f 5a f8 mov -1958(r15),512(r15);0xf85a(r15), 0x0200(r15)
f824: 00 02
f826: f8 23 jnz $-14 ;abs 0xf818
@@ -38,10 +38,19 @@
f842: fd 3f jmp $-4 ;abs 0xf83e
0000f844 <__ctors_end>:
- f844: 30 40 48 f8 br #0xf848
+ f844: 30 40 58 f8 br #0xf858
-0000f848 <_unexpected_>:
- f848: 00 13 reti
+0000f848 <main>:
+ f848: 04 12 push r4
+ f84a: 04 41 mov r1, r4
+ f84c: 21 83 decd r1
+ f84e: 84 43 fe ff mov #0, -2(r4) ;r3 As==00, 0xfffe(r4)
+ f852: 00 3c jmp $+2 ;abs 0xf854
+ f854: ff 3f jmp $+0 ;abs 0xf854
+ ...
+
+0000f858 <_unexpected_>:
+ f858: 00 13 reti
Disassembly of section .vectors: But compiling the following Rust program: #![feature(lang_items)]
#![feature(no_core)]
#![no_core]
#![no_main]
#[export_name = "main"]
pub fn main() -> ! {
loop {}
}
#[lang = "copy"]
trait Copy {}
#[lang = "sized"]
trait Sized {} produces the same disassembly as the empty C source file. This means that the |
I can get a MSP430 board tomorrow, to do some tests. |
It doesn't right now. We'll have to add a new ABI, e.g. |
Could you elaborate about what this does? Is a gcc |
It modifies the value on the stack that is located one word below the current frame (I don't know the right words how to say this). Basically when CPU enters interrupt it does this:
As far as I know this is the only way to modify the status register and exit low power mode. |
@japaric |
The built-in looks like a function call that goes inside an "interrupt handler"? Something like this: __irq void foo() {
// ..
__bic_SR_register_on_exit();
}
Maybe. But I think that if you use a naked function then you have to manually modify the stack pointer during the prologue/epilogue of the function according to the number of local variables you use. My worry is that this approach may require the (final) user to deal with low level ABI-ish details. |
Or is an attribute? |
Yes, it looks and feels like normal function. Synopsisunsigned __bic_SR_register_on_exit(unsigned mask); Description
Note
|
I think it is not possible to write llvm IR that will do this kind of thing. So this function should be added as intrinsic in llvm (and as built-in in clang). and only then we can have it in rust. |
rust-lang/rust#37672 has landed and the MSP430 LLVM backend should be enabled in the next nightly. People should be able to experiment with custom targets out of tree with that nightly. |
I've got a couple of MSP430 fram boards. I'll try out a blinky tomorrow. On Tue, Nov 15, 2016, 23:06 Jorge Aparicio [email protected] wrote:
|
So I tried to compile core and I get this errors from assembler:
the code on line 9468 looks like this: _ZN4core3num7dec2flt5rawfp10prev_float14_MSG_FILE_LINE17h5bcd06a3b0d78233E:
.short str.3@
.short 27
.short str.1v I tried both
|
Hi! |
Anyone working on this |
@pftbest how did you get around the LLVM assertion compiling core? |
@japaric re: the linker deleting everything, the problem seems to be
|
OK, so |
So I fixed the issue in LLVM, but it is not committed in upstream yet. See #37829. Don't know how long it will take to get it into rust. Alternatively, you can just disable the assertions in
There should be a better way to fix this, because without |
I didn't see this issue when compiling core but I used msp430-gcc 6.2.0. If
|
Alternatively, we can pass |
After re-checking with mps430-gcc 6.2, I see that core doesn't compile due to the "@" business. Weird, I tought that was already working |
Submitted #38116 |
Finally managed to blink a real LED on MSP430 board. The code is here, but unfortunately, you need a patched |
Nice work, @pftbest! re: MSP430 and MSP430X. Is re: -nodefaultlibs. In theory we could implement the startup stuff in Rust to not depend on the startup objects that get (implicitly?) passed to the linker when -nodefaultlibs is not used thus achieving pure Rust programs like in the Cortex-M case. In any case, it makes sense to set |
Well, I think the only option that can change in the near future is |
@japaric, Can rust use llvm feature flags? Or is there any other preferred way to handle codegen options? So the problem is that some micros have hardware multiplier and some don't, and also there is 2 types of multipliers. |
Yes, I am working on adding integrated assembler support, though I have no immediate ETA. We should still support external assemblers though - this can be overridden, right? Let me make sure I understand the proposed targets. There are four sets of flags - rustc, llvm, assembler, and linker. We can obviously control rustc flags, and linker flags are said (above) to be able to be modified with RUSTFLAGS. Assembler flags are in the target.json, and I don't know about llvm (llc) flags. The flags for MSP430s are
If we can pass llvm options outside the target.json but not assembly, we only need 2 targets. Otherwise, we need 8, which I think is too many. Also - what is the expectation for targets in the compiler in terms of functionality/stability? For example, I wouldn't trust the extended instructions as far as I could throw them right now (assuming they can be generated at all, which I doubt). Does that mean we should only add the vanilla target? |
The scenario in that case would be: LLVM learns to emit object files and we no longer need to use an external assembler. rustc would directly generate object files and still depend on an external linker but the asm-args flag could be dropped. The target specification would be updated to change no-integrated-as to true, drop the asm-args and to emit msp430 (not msp430x) object files.
There's
In my mind we only need as many targets as the number of ABIs that exist. Here by ABI I mean something like ARM's soft float vs hard float ABIs where you can't link two objects unless they have the same ABI. My main concern here is being able to link to TI's startup objects as that's required for all programs (AFAIK). IIRC, there are two ABIs in that sense: msp430 and msp430x and that's why we had to add the no-integrated-as option: the external assembler produced msp430x code by default and that couldn't be linked to the startup object which used the msp430 ABI; the no-integrated-as let us change the ABI of the Rust side to msp430. |
There are several issues here. First, the CPU core. TI produces two different CPU cores under the larger MSP430 "umbrella" brand. msp430 cores have 16-bit registers and can access a 64k address space. msp430x cores have 20-bit registers (mostly) and can access a 1M address space. msp430x cores also support an extended ISA which includes instructions for manipulating 20-bit registers and data values as well as some useful utility instructions like multi-push and multi-pop. libgcc builds two different versions, one for msp430x and one for msp430, the difference presumably being that the msp430x one uses the more-efficient extended instructions. Second, the hardware multiplier. Depending on the hwmult option passed to LLVM, it will emit different library calls for integer multiplication as specified in the EABI. These functions are defined in libgcc (eventually also compiler-rt), but in separate .a files (libmul_16.a, libmul_32.a, etc). So everything uses the same ABI, calling convention, ISA, etc., but different library calls are generated and linked. Then there's the memory models, large and small. This has to do with whether address spaces >64k are supported, and whether pointers are 16-bit or 20-bit. libgcc also builds a large-model version of itself for linking against large memory model programs. Since only msp430x CPUs support 20-bit pointers, this makes 3 versions of libgcc (and libmul_*) - small/430, small/430x, large/430x. Also, there used to be two different MSP430 ABIs - one was in the old mspgcc, a heavily patched fork of GCC 4, while the newer one is in TI's CCS, IAR's Embedded Workbench, and upstream GCC. LLVM used to use the mspgcc ABI, but I've since patched it to use the newer ABI, since mspgcc is effectively dead. So this can be ignored now. Finally, the startup object (crt0.o) is built as part of newlib usually, and has a large and small version but no differences based on CPU type. So just large and small. You can link msp430 code to msp430x code as long as it's run on an msp430x CPU. You can't link large and small objects together (you might be able to but it's a nightmare). You can link to either the hwmult library you have or the software version (libmul_none.a), but you can't link to a hwmult you don't have. Total, this gives (small/large/430)*(none/16/32/f5) is 12 configurations, eventually. The default configuration is small/none. The chip used by @pftbest in his repo is an msp430 CPU, so it needed -mcpu=msp430 in order to link to the proper libraries (msp430/none). Personally I don't see why we can't have just one target and specify all the other stuff on the command line. Currently there's only one target triple in both LLVM and GCC. |
In theory this is possible, but the linker will not allow it:
So they should be a separate targets. Multiplier option on the other hand is not so strict, for example, gcc will happily link two object files, where one was compiled with What can be done about small/large memory models I'm not sure yet. |
You're right, I don't know why I didn't notice the 430/ folder for crt0. Curious that they chose to do this but I suppose it would almost always be an error. |
I think we would need only 3 targets in the end:
But right now we only have the first one working, so if we are going to merge something, I think we should only do the msp430. |
Possibly because all the operations that would involve the hardware mulitplier get lowered to calls to intrinsics by gcc and are left undefined in object files. Thus the only -mhwmult that matters is the one used to link the final binary; that one selects the appropriate libmul_foo.a library. If that hypothesis is correct then today, where the intrinsics are written in C and packaged in libmul_foo.a, the way to select the hwmult setting is via the In a 100% Rust scenario hwmult selection could be emulated by having Cargo features in the compiler-builtins crate (that crate only appears once and at the bottom of the dependency graph); in that scenario, targets won't have a hwmult setting and the user would pick the hwmult through a Cargo feature (e.g. in Xargo.toml). So yeah it sounds to me that at the end we'll want the three targets that @pftbest mentioned.
Sounds reasonable to me. |
I agree all of with the above. |
We have some new ABI related bugs. Also this C code doesn't follow EABI void gg(char a, char b, char c, char d, char e, char f);
void kk() {
gg(1, 2, 3, 4, 5, 6);
} gcc:
llvm:
|
The latter issue seems to be due to the promotion of i8s to i16s in AnalyzeVarArgs in MSP430ISelLowering.cpp. We might be able to just delete that whole block and call it done. |
I found it because of this comment in callingconv.td
The first issue is indeed a bug in our data layout string. It shouldn't have |
There is an exception for varargs in EABI, they should be promoted to i16 still. |
Hello, @awygle. |
Note that cortex-m-rt does nothing special about interrupts before main. However, Cortex-M devices behave like this: on reset all the interrupts are unmasked (PRIMASK = 0), but each interrupt must be enabled (using the NVIC) before it can be used. With code: fn main() {
// ..
nvic.set_pending(Interrupt::TIM2); // no effect
nvic.enable(Interrupt::TIM3);
nvic.set_pending(Interrupt::TIM3); // ISR preempts main
nvic.enable(Interrupt::TIM4);
interrupt::disable(); // PRIMASK = 1 (_masks_ all interrupts)
nvic.set_pending(Interrupt::TIM4); // no effect
} IMO, msp430-rt shouldn't do anything special to try to match the behavior of Cortex-M devices; msp430-rt should preserve the default behaviar of the MSP430 architecture. |
Hi @pftbest, All peripherals should be reset after MCU reset. However, be aware that LPM3.5 allows the RTC module to stay active but triggers what looks like a reset on wakeup. See pages 60 and 61 of http://www.ti.com/lit/ug/slau367n/slau367n.pdf for more details. Basically, it will reset itself but that might not be what you actually want. |
Well, then we would always have a one more unsafe block inside the main(). And in general I think that current system doesn't work too well with MSP430. fn main() {
// interrupts are disabled here, `free` is useless
interrupt::free(|cs| {
let timer = TIMER0_A3.borrow(cs);
// ...
});
// mandatory unsafe block
unsafe {
interrupt::enable();
}
loop {}
}
interrupt!(TIMER0_A1, timer_handler);
fn timer_handler() {
// interrupts are disabled here, so this `free` is also useless
interrupt::free(|cs| {
let timer = TIMER0_A3.borrow(cs);
// ...
});
} I think a better system for MSP430 may look like this: fn main(cs: CSToken) { // cs token by value
let timer = TIMER0_A3.borrow(&cs); // borrow it to modify registers
// ...
// when we are done, we can give up the token to enable interrupts
interrupt::enable(cs); // no unsafe block required
// here we would need the `free` function, since we don't have a token anymore
interrupt::free(|cs| {
// ...
});
loop {}
}
interrupt!(TIMER0_A1, timer_handler);
fn timer_handler(cs: CSToken, sr: SavedSr) { // cs token by value, sr for LPM support
let timer = TIMER0_A3.borrow(&cs);
// ...
// if we want to enable interrupts inside an interrupt handler,
// same story
interrupt::enable(cs); // no unsafe block required
// this thing will do __bic_sr_on_exit
sr.exit_lpm();
} |
Sure. I don't really like or directly use the cortex-m-quickstart system either. Quickstart is meant to give you one way, ideally the simplest way (vanilla cortex-m-rt and svd2rust should serve as a good substrate to build better systems to achieve data race freedom though. The better system you mention could be achieved with macros like I have mentioned you before. Something like this: app! {
device: msp430g2553,
init: init,
idle: idle,
}
// full access to all peripherals
fn init(p: msp430g2553::Peripherals) {
p.PORT1.write(..);
}
fn idle() -> ! {
loop {
interrupt::free(|cs| {
// do something
});
// do something else
}
} where fn main() {
init(unsafe { msp430g2553::Peripherals::all() });
unsafe { interrupt::enable() }
idle()
} Of course you could also modify the msp430 crate to approach the system you've described. You are the maintainer of those crates; you make the decisions here. |
This definition is wrong, because the offset on the stack is not a constant. See this link for some ideas for how to actually do it: rust-embedded/wg#20 (comment)
Triage (last WG meeting). The goal is to have a rust-std component working on stable plus up to date svd2rust support by the
Compiler support for interrupts will be left unstable for now. The MSP430 community can explore |
I would add:
Does this mean up-to-date RTFM is a non-goal as well? |
@pftbest this thread has gotten too long. Could you make new issues for the stuff you want to get done by the edition release and assign them tentative milestones (RC1, RC2; see this repo milestones)? Upstream stuff like rust-std components and LLVM bugs should be filled here; everything else should be filled in their own repo. @cr1901 RTFM is not a blocker / priority for the edition release. |
Parent issue: #2
Next steps:
etc. See enable the MSP430 LLVM backend rust-lang/rust#37672
readelf -h
andobjdump -Cd
on your (working) programs. LED blinking examplecore
for the MSP430 architecture rust-lang/rust#37829. It block us from compiling the core crateextern "msp430-interrupt" fn
. (Done in calling convention for MSP430 interrupts rust-lang/rust#38465)__bic_SR_register_on_exit
gcc built-in somehow. Needed to enter/leave low-power mode.programs for this target. (See https://github.com/pftbest/rust_on_msp)
The text was updated successfully, but these errors were encountered: