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
Initial support for nRF51822 #17
Changes from all commits
d71d43c
604db4b
1715c66
852b757
8affa07
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
$(BUILD_DIR)/arch.o: $(SRC_DIR)arch/$(ARCH)/ctx_switch.S $(SRC_DIR)arch/$(ARCH)/syscalls.S | ||
@$(TOOLCHAIN)as -mcpu=$(ARCH) -mthumb $^ -o $@ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
.cpu cortex-m0 | ||
.syntax unified | ||
.thumb | ||
.text | ||
|
||
/* Exported functions */ | ||
.global SVC_Handler | ||
.globl switch_to_user | ||
|
||
.thumb_func | ||
SVC_Handler: | ||
ldr r0, EXC_RETURN_MSP | ||
cmp lr, r0 | ||
bne to_kernel | ||
ldr r1, EXC_RETURN_PSP | ||
bx r1 | ||
|
||
to_kernel: | ||
mrs r0, PSP /* PSP into r0 */ | ||
str r0, [sp, #0] /* PSP into Master stack r0 */ | ||
ldr r1, EXC_RETURN_MSP | ||
bx r1 | ||
|
||
EXC_RETURN_MSP: | ||
.word 0xFFFFFFF9 | ||
EXC_RETURN_PSP: | ||
.word 0xFFFFFFFD | ||
|
||
.thumb_func | ||
/* r0 is top of user stack, r1 is heap base */ | ||
switch_to_user: | ||
/* Load bottom of stack into Process Stack Pointer */ | ||
msr psp, r0 | ||
|
||
/* Cortex-M0 can only push registers R0-R7 directly, so move R8-R11 to R0-R3. | ||
* This is equivalent to the 32-bit "push {r4-r11}" instruction. */ | ||
push {r4-r7} | ||
mov r4, r8 | ||
mov r5, r9 | ||
mov r6, r10 | ||
mov r7, r11 | ||
push {r4-r7} | ||
|
||
mov r9, r1 | ||
svc 0xff | ||
|
||
/* These instructions are equivalent to the 32-bit "pop {r4-r11}" */ | ||
pop {r4-r7} | ||
mov r8, r4 | ||
mov r9, r5 | ||
mov r10, r6 | ||
mov r11, r7 | ||
pop {r4-r7} | ||
|
||
bx lr |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
.cpu cortex-m0 | ||
.syntax unified | ||
.thumb | ||
.text | ||
|
||
.section .syscalls | ||
|
||
/* Cortex-M0 can only push/pop registers R0-R7 directly, so move R8-R11 to/from | ||
* R0-R3. This is equivalent to the 32-bit "push/pop {r4-r11}" instructions. */ | ||
|
||
.macro SYSCALL NAME, NUM | ||
.global \NAME | ||
.thumb_func | ||
\NAME : | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this trick. Perhaps it would make sense to have each architecture define just the macro, and then call the macro (as you do below in lines 31-34) so there is no chance of getting the syscall numbers inconsistent. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used this porting task for testing these fancy features :) I can do that once I confirm that __wait() does indeed not need to save/restore LR (did you test removing it from the push/pop call for storm?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't tested that yet |
||
push {r4-r7} | ||
mov r4, r8 | ||
mov r5, r9 | ||
mov r6, r10 | ||
mov r7, r11 | ||
push {r4-r7} | ||
svc \NUM | ||
pop {r4-r7} | ||
mov r8, r4 | ||
mov r9, r5 | ||
mov r10, r6 | ||
mov r11, r7 | ||
pop {r4-r7} | ||
bx lr | ||
.endm | ||
|
||
SYSCALL __wait, 0 | ||
SYSCALL __subscribe, 1 | ||
SYSCALL __command, 2 | ||
SYSCALL __allow, 3 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
ARCH = cortex-m0 | ||
|
||
RUSTC_FLAGS += -C opt-level=3 -Z no-landing-pads | ||
RUSTC_FLAGS += --target $(SRC_DIR)chips/$(CHIP)/target.json | ||
RUSTC_FLAGS += -Ctarget-cpu=$(ARCH) -C relocation_model=static | ||
RUSTC_FLAGS += -g -C no-stack-check | ||
|
||
CFLAGS += -g -O3 -std=gnu99 -mcpu=$(ARCH) -mthumb -nostdlib -T$(SRC_DIR)chips/$(CHIP)/loader.ld | ||
LDFLAGS += -mcpu=$(ARCH) -mthumb | ||
LOADER = $(SRC_DIR)chips/$(CHIP)/loader.ld | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like we may be able to get away with having most/all of this file unified for all chips and just rely on variables for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe moved upper to src/chips/Makefile.mk? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest TOCK_CHIP and TOCK_ARCH; that way you can pull them from environment variables if needed. |
||
|
||
$(BUILD_DIR)/lib$(CHIP).rlib: $(call rwildcard,$(SRC_DIR)chips/$(CHIP),*.rs) $(BUILD_DIR)/libcore.rlib $(BUILD_DIR)/libhil.rlib $(BUILD_DIR)/libcommon.rlib | ||
@echo "Building $@" | ||
@$(RUSTC) $(RUSTC_FLAGS) --out-dir $(BUILD_DIR) $(SRC_DIR)chips/$(CHIP)/lib.rs | ||
|
||
$(BUILD_DIR)/crt1.o: $(SRC_DIR)chips/$(CHIP)/crt1.c | ||
@echo "Building $@" | ||
@$(CC) $(CFLAGS) -c $< -o $@ -lc -lgcc | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
/* | ||
* 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/>. | ||
* | ||
* Copyright 2014, Michael Andersen <m.andersen@eecs.berkeley.edu> | ||
*/ | ||
|
||
#include <stdint.h> | ||
#include <string.h> | ||
|
||
/* Symbols defined in the linker file */ | ||
extern uint32_t _estack; | ||
extern uint32_t _etext; | ||
extern uint32_t _szero; | ||
extern uint32_t _ezero; | ||
extern uint32_t _srelocate; | ||
extern uint32_t _erelocate; | ||
|
||
int main(void); | ||
|
||
void Dummy_Handler(void) | ||
{ | ||
while (1) { | ||
} | ||
} | ||
|
||
void Reset_Handler(void); | ||
void NMI_Handler(void) __attribute__ ((weak, alias("Dummy_Handler"))); | ||
void HardFault_Handler(void) | ||
__attribute__ ((weak, alias("Dummy_Handler"))); | ||
void SVC_Handler(void) __attribute__ ((weak, alias("Dummy_Handler"))); | ||
void PendSV_Handler(void) __attribute__ ((weak, alias("Dummy_Handler"))); | ||
void SysTick_Handler(void) __attribute__ ((weak, alias("Dummy_Handler"))); | ||
|
||
typedef void (*interrupt_function_t) (void); | ||
|
||
__attribute__ ((section(".vectors"))) | ||
interrupt_function_t interrupt_table[] = { | ||
(interrupt_function_t) (&_estack), | ||
Reset_Handler, | ||
NMI_Handler, | ||
HardFault_Handler, | ||
0, 0, 0, 0, 0, 0, 0, /* Reserved */ | ||
SVC_Handler, | ||
0, 0, /* Reserved */ | ||
PendSV_Handler, | ||
SysTick_Handler, | ||
}; | ||
|
||
void Reset_Handler(void) | ||
{ | ||
uint32_t *pSrc, *pDest; | ||
|
||
/* Power on RAM blocks manually (see nRF51822-PAN v2.4, PAN #16). Note | ||
* that xxAA/xxAB variants have only two RAM blocks. For xxAC, change | ||
* to 0x0F. */ | ||
*((uint32_t volatile * ) 0x40000524) = 0x03; | ||
|
||
/* Move the relocate segment | ||
* This assumes it is located after the | ||
* text segment, which is where the storm | ||
* linker file puts it | ||
*/ | ||
pSrc = &_etext; | ||
pDest = &_srelocate; | ||
|
||
if (pSrc != pDest) { | ||
for (; pDest < &_erelocate;) { | ||
*pDest++ = *pSrc++; | ||
} | ||
} | ||
|
||
/* Clear the zero segment */ | ||
for (pDest = &_szero; pDest < &_ezero;) { | ||
*pDest++ = 0; | ||
} | ||
|
||
/* Branch to main function */ | ||
main(); | ||
} | ||
|
||
// IMPORTANT!! __aeabi_memset has count and value arguments reversed from ANSI | ||
// memset. TODO(alevy): Why does arm-none-eabi's libc not have __aeabi_memset? | ||
__attribute__ ((weak)) | ||
void __aeabi_memset(void *dest, size_t count, int value) | ||
{ | ||
memset(dest, value, count); | ||
} | ||
|
||
__attribute__ ((weak)) | ||
extern void __aeabi_memcpy(void *dest, void *src, unsigned int n) | ||
{ | ||
memcpy(dest, src, n); | ||
} | ||
|
||
__attribute__ ((weak)) | ||
extern void __aeabi_memcpy4(void *dest, void *src, unsigned int n) | ||
{ | ||
memcpy(dest, src, n); | ||
} | ||
|
||
__attribute__ ((weak)) | ||
extern void __aeabi_memcpy8(void *dest, void *src, unsigned int n) | ||
{ | ||
memcpy(dest, src, n); | ||
} | ||
|
||
__attribute__ ((weak)) | ||
extern void __aeabi_memclr(void *dest, size_t n) | ||
{ | ||
memset(dest, 0, n); | ||
} | ||
|
||
__attribute__ ((weak)) | ||
extern void __aeabi_memclr4(void *dest, size_t n) | ||
{ | ||
memset(dest, 0, n); | ||
} | ||
|
||
__attribute__ ((weak)) | ||
extern void __aeabi_memclr8(void *dest, size_t n) | ||
{ | ||
memset(dest, 0, n); | ||
} | ||
|
||
/* Based on reference code from GCC documentation, see "Legacy __sync Built-in | ||
* Functions for Atomic Memory Access" */ | ||
|
||
__attribute__ ((weak)) | ||
extern uint32_t __sync_fetch_and_add_4(uint32_t * ptr, uint32_t val) | ||
{ | ||
uint32_t tmp = *ptr; | ||
*ptr += val; | ||
return tmp; | ||
} | ||
|
||
__attribute__ ((weak)) | ||
extern uint32_t __sync_fetch_and_sub_4(uint32_t * ptr, uint32_t val) | ||
{ | ||
uint32_t tmp = *ptr; | ||
*ptr -= val; | ||
return tmp; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
use core::mem; | ||
use core::ops::{Index, IndexMut}; | ||
use hil; | ||
|
||
// Source: https://github.com/hackndev/zinc/tree/master/volatile_cell | ||
#[derive(Copy, Clone)] | ||
#[repr(C)] | ||
pub struct VolatileCell<T> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this abstraction. We should consider pulling it out and using it more widely There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed! |
||
value: T, | ||
} | ||
|
||
#[allow(dead_code)] | ||
impl<T> VolatileCell<T> { | ||
#[inline] | ||
pub fn get(&self) -> T { | ||
unsafe { | ||
::core::intrinsics::volatile_load(&self.value) | ||
} | ||
} | ||
|
||
#[inline] | ||
pub fn set(&self, value: T) { | ||
unsafe { | ||
::core::intrinsics::volatile_store(&self.value as *const T as *mut T, value) | ||
} | ||
} | ||
} | ||
|
||
#[allow(non_snake_case)] | ||
struct GPIO { | ||
_pad0: [u8; 1284], | ||
pub OUT: VolatileCell<u32>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. field names should have |
||
pub OUTSET: VolatileCell<u32>, | ||
pub OUTCLR: VolatileCell<u32>, | ||
pub IN: VolatileCell<u32>, | ||
pub DIR: VolatileCell<u32>, | ||
pub DIRSET: VolatileCell<u32>, | ||
pub DIRCLR: VolatileCell<u32>, | ||
_pad1: [u8; 480], | ||
pub PIN_CNF: [VolatileCell<u32>; 32], | ||
} | ||
|
||
#[allow(non_snake_case)] | ||
fn GPIO() -> &'static GPIO { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The connonical way of doing this is: impl GPIO {
fn new() -> &'static GPIO {
unsafe { mem::transmute(0x50000000 as usize) }
}
} However, this might be a nice alternative. As I'm suggesting, the obvious thing to do would be to stick the result in a static variable, at which point the contents has to remain an unsafe pointer because we can't call This actually ends up being a reasonably nice interface. |
||
unsafe { mem::transmute(0x50000000 as usize) } | ||
} | ||
|
||
pub struct GPIOPin { | ||
pin: u8, | ||
} | ||
|
||
impl GPIOPin { | ||
const fn new(pin: u8) -> GPIOPin { | ||
GPIOPin { pin: pin } | ||
} | ||
} | ||
|
||
impl hil::gpio::GPIOPin for GPIOPin { | ||
fn enable_output(&self) { | ||
GPIO().PIN_CNF[self.pin as usize].set((1 << 0) | (1 << 1) | (0 << 2) | (0 << 8) | (0 << 16)); | ||
} | ||
|
||
fn enable_input(&self, _mode: hil::gpio::InputMode) { | ||
unimplemented!(); | ||
} | ||
|
||
fn disable(&self) { | ||
unimplemented!(); | ||
} | ||
|
||
fn set(&self) { | ||
GPIO().OUTSET.set(1 << self.pin); | ||
} | ||
|
||
fn clear(&self) { | ||
GPIO().OUTCLR.set(1 << self.pin); | ||
} | ||
|
||
fn toggle(&self) { | ||
unimplemented!(); | ||
} | ||
|
||
fn read(&self) -> bool { | ||
unimplemented!(); | ||
} | ||
|
||
fn enable_interrupt(&self, _identifier: usize, _mode: hil::gpio::InterruptMode) { | ||
unimplemented!(); | ||
} | ||
} | ||
|
||
pub struct Port { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the nrf51822 has only one port, so the abstraction into a |
||
pins: [GPIOPin; 32] | ||
} | ||
|
||
impl Index<usize> for Port { | ||
type Output = GPIOPin; | ||
|
||
fn index(&self, index: usize) -> &GPIOPin { | ||
&self.pins[index] | ||
} | ||
} | ||
|
||
impl IndexMut<usize> for Port { | ||
fn index_mut(&mut self, index: usize) -> &mut GPIOPin { | ||
&mut self.pins[index] | ||
} | ||
} | ||
|
||
pub static mut PA : Port = Port { | ||
pins: [ | ||
GPIOPin::new(0), GPIOPin::new(1), GPIOPin::new(2), GPIOPin::new(3), | ||
GPIOPin::new(4), GPIOPin::new(5), GPIOPin::new(6), GPIOPin::new(7), | ||
GPIOPin::new(8), GPIOPin::new(9), GPIOPin::new(10), GPIOPin::new(11), | ||
GPIOPin::new(12), GPIOPin::new(13), GPIOPin::new(14), GPIOPin::new(15), | ||
GPIOPin::new(16), GPIOPin::new(17), GPIOPin::new(18), GPIOPin::new(19), | ||
GPIOPin::new(20), GPIOPin::new(21), GPIOPin::new(22), GPIOPin::new(23), | ||
GPIOPin::new(24), GPIOPin::new(25), GPIOPin::new(26), GPIOPin::new(27), | ||
GPIOPin::new(28), GPIOPin::new(29), GPIOPin::new(30), GPIOPin::new(31), | ||
], | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh great! So this will test compilation for both platforms. I think
APPS=c_blinky
might be somewhat extraneous since it just gets compiled to a blob and linked -- there is nothing really specific to this platform here.But no matter, it's at least a place for now to document how to get an example that actually runs on the board.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once UART is implemented for this board, I can simply drop APPS and still have a functional default build. Should be my next task.