I've made use of one library (crate) called avrd which only provides address mapping for ATMega328ATMega328P microcontroller.
#![no_std]
#![no_main]
#![feature(asm_experimental_arch)]
use core::{arch::asm, hint::black_box, panic::PanicInfo};
use avrd::atmega328;atmega328p;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
#[inline(never)]
fn delay(x: u32) {
for _ in 0..x {
unsafe {
asm!("nop");
}
}
}
unsafe fn write_reg(reg: *mut u8, val: u8, mask: u8) {
let reg_val = reg.read_volatile();
reg.write_volatile((reg_val & !mask) | (val & mask));
}
#[no_mangle]
extern "C" fn main() -> ! {
const LED_BUILTIN: u8 = 5;
unsafe {
let portB_data_direction = atmega328atmega328p::DDRB;
// set it to output mode
write_reg(portB_data_direction, 1 << LED_BUILTIN, 1 << LED_BUILTIN);
let portB = atmega328atmega328p::PORTB;
// switch it on, hopefully..
loop {
write_reg(portB, 1 << LED_BUILTIN, 1 << LED_BUILTIN);
delay(2500_0000);
write_reg(portB, 0, 1 << LED_BUILTIN);
loop { delay(500_0000);
}
}
}
#[no_mangle]
extern "C" fn main() -> ! {
const LED_BUILTIN: u8 = 5;
unsafe {
let portB_data_direction = atmega328atmega328p::DDRB;
// set it to output mode
write_reg(portB_data_direction, 1 << LED_BUILTIN, 1 << LED_BUILTIN);
let portB = atmega328atmega328p::PORTB;
// switch it on, hopefully..
let mut i = 0;
loop {
while i < 1000000 {
i += 1;
write_reg(portB, 1 << LED_BUILTIN, 1 << LED_BUILTIN);
}
i = 0;
while i < 1000000 {
i += 1;
write_reg(portB, 0, 1 << LED_BUILTIN);
}
i = 0;
}
}
}
[build]
target = "avr-unknown-gnu"atmega328p.json" # Plucked from https://github.com/Rahix/avr-atmega328"hal/
[unstable]
build-std = ["core"]
[target.'cfg(target_arch = "avr")']
runner = "ravedude uno --baudrate 57600"
Note that the microcontroller inside Arduino Uno is ATMega328P, while the MCU I've selected in config.toml is ATMega328. But looking it up online seems to suggest that both the chips are mostly the same, and ATMega328P might have more instructions available. Hence it's a superset of the chip I'm generating the code for. But I'm not totally sure of this and my assumption could be wrong.
Oh, and another thing. The AVR toolchain that Rust is consuming is the same one that comes with Arduino, I haven't installed any separately (mentioning in case that toolchain causes problems on non-C platforms).
Here's the disassembly for the first code snippet (RUSTFLAGS="--emit asm" cargo run --release, not the final linked assembly):
.text
.set __tmp_reg__, 0
.set __zero_reg__, 1
.set __SREG__, 63
.set __SP_H__, 62
.set __SP_L__, 61
.file "arduino_blink.4f63ed9bfc966480caf25912130a4f-cgu.0"
.section .text._ZN13arduino_blink5delay17h211bbf3c0743033cE_ZN13arduino_blink5delay17h9627a982856e7dadE,"ax",@progbits
.p2align 1
.type _ZN13arduino_blink5delay17h211bbf3c0743033cE_ZN13arduino_blink5delay17h9627a982856e7dadE,@function
_ZN13arduino_blink5delay17h211bbf3c0743033cE_ZN13arduino_blink5delay17h9627a982856e7dadE:
ldi r24, 0
ldi r25, 0
movwldi r18, 75
r18 ldi r20, r2476
ldi r21, 0
movw r20r22, r24
.LBB0_1:
ldi r22r19, 1
cpi r18r24, 264
cpc r19r25, r1r18
cpc r20r22, r24r20
cpc r21r23, r25r21
brlo .LBB0_3
mov r22r19, r1
.LBB0_3:
andi r22r19, 1
cpi r22r19, 0
breq .LBB0_5
subi r18r24, 255
sbci r19r25, 255
sbci r20r22, 255
sbci r21r23, 255
;APP
nop
;NO_APP
rjmp .LBB0_1
.LBB0_5:
ret
.Lfunc_end0:
.size _ZN13arduino_blink5delay17h211bbf3c0743033cE_ZN13arduino_blink5delay17h9627a982856e7dadE, .Lfunc_end0-_ZN13arduino_blink5delay17h211bbf3c0743033cE_ZN13arduino_blink5delay17h9627a982856e7dadE
.section .text.main,"ax",@progbits
.globl main
.p2align 1
.type main,@function
main:
sbi 4, 5
.LBB1_1:
sbi 5, 5
call _ZN13arduino_blink5delay17h211bbf3c0743033cE_ZN13arduino_blink5delay17h9627a982856e7dadE
cbi 5, 5
.LBB1_1: call _ZN13arduino_blink5delay17h9627a982856e7dadE
rjmp .LBB1_1
.Lfunc_end1:
.size main, .Lfunc_end1-main