Skip to content

Commit 1e25d1d

Browse files
committed
Support auto-generated cores
1 parent bdc6a30 commit 1e25d1d

File tree

10 files changed

+521
-4
lines changed

10 files changed

+521
-4
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ description = """
1313
Reusable components for the Arduino Uno.
1414
"""
1515
keywords = ["avr", "arduino", "uno"]
16+
17+
[build-dependencies]
18+
avr-mcu = "0.2"
19+

avr-atmega328p.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"llvm-target": "avr-unknown-unknown",
3+
"cpu": "atmega328p",
4+
"target-endian": "little",
5+
"target-pointer-width": "16",
6+
"os": "unknown",
7+
"target-env": "",
8+
"target-vendor": "unknown",
9+
"arch": "avr",
10+
"data-layout": "e-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
11+
12+
"executables": true,
13+
14+
"linker": "avr-gcc",
15+
"linker-flavor": "gcc",
16+
"pre-link-args": {
17+
"gcc": ["-Os", "-mmcu=atmega328p"]
18+
},
19+
"exe-suffix": ".elf",
20+
"post-link-args": {
21+
"gcc": ["-Wl,--gc-sections"]
22+
},
23+
24+
"no-default-libraries": false
25+
}
26+

build.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
extern crate avr_mcu;
2+
3+
use avr_mcu::*;
4+
use std::fs::{self, File};
5+
use std::io;
6+
use std::io::prelude::*;
7+
use std::path::{Path, PathBuf};
8+
9+
fn cores_path() -> PathBuf {
10+
Path::new(env!("CARGO_MANIFEST_DIR")).join("src").join("cores")
11+
}
12+
13+
fn core_module_name(mcu: &Mcu) -> String {
14+
mcu.device.name.to_lowercase().to_owned()
15+
}
16+
17+
fn main() {
18+
if !cores_path().exists() {
19+
fs::create_dir_all(&cores_path()).expect("could not create cores directory");
20+
}
21+
22+
let current_mcu = avr_mcu::current::mcu()
23+
.expect("no target cpu specified");
24+
generate_cores(&[current_mcu]).unwrap()
25+
}
26+
27+
fn generate_cores(mcus: &[Mcu]) -> Result<(), io::Error> {
28+
for mcu in mcus {
29+
generate_core_module(mcu).expect("failed to generate mcu core");
30+
}
31+
generate_cores_mod_rs(mcus)
32+
}
33+
34+
fn generate_core_module(mcu: &Mcu) -> Result<(), io::Error> {
35+
let path = cores_path().join(format!("{}.rs", core_module_name(mcu)));
36+
let mut file = File::create(&path)?;
37+
write_core_module(mcu, &mut file)
38+
}
39+
40+
fn generate_cores_mod_rs(mcus: &[Mcu]) -> Result<(), io::Error> {
41+
let path = cores_path().join("mod.rs");
42+
let mut w = File::create(&path)?;
43+
44+
writeln!(w, "//! Cores")?;
45+
writeln!(w)?;
46+
for mcu in mcus {
47+
writeln!(w, "/// The {}.", mcu.device.name)?;
48+
writeln!(w, "pub mod {};", core_module_name(mcu))?;
49+
}
50+
writeln!(w)
51+
}
52+
53+
fn write_core_module(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> {
54+
writeln!(w, "//! Core for {}.", mcu.device.name)?;
55+
writeln!(w)?;
56+
writeln!(w, "use {{HardwareSpi, Pin, Register}};")?;
57+
writeln!(w)?;
58+
59+
gen::write_registers(mcu, w)?;
60+
gen::write_pins(mcu, w)?;
61+
gen::write_spi_modules(mcu, w)?;
62+
63+
writeln!(w)
64+
}
65+
66+
mod gen {
67+
use avr_mcu::*;
68+
use std::io;
69+
use std::io::prelude::*;
70+
71+
pub fn write_registers(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> {
72+
for register in mcu.registers() {
73+
let ty = if register.size == 1 { "u8" } else { "u16" };
74+
75+
// HACK: Skip, atmeg328p pack defines two of these.
76+
if register.name == "GTCCR" { continue; }
77+
78+
writeln!(w, "pub struct {};", register.name)?;
79+
writeln!(w, "impl Register<{}> for {} {{", ty, register.name)?;
80+
writeln!(w, " const ADDR: *mut {} = 0x{:x} as *mut {};", ty, register.offset, ty)?;
81+
writeln!(w, "}}")?;
82+
}
83+
84+
Ok(())
85+
}
86+
87+
pub fn write_pins(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> {
88+
if let Some(port) = mcu.peripheral("PORT") {
89+
for instance in port.instances.iter() {
90+
for signal in instance.signals.iter() {
91+
let idx = signal.index.expect("signal with no index");
92+
let struct_name = pin_name(instance, signal);
93+
94+
let io_module = mcu.modules.iter().find(|m| m.name == "PORT")
95+
.expect("no port io module defined for this port");
96+
let register_group = io_module.register_groups.iter()
97+
.find(|rg| rg.name == instance.name)
98+
.expect("no register group defined for this port");
99+
100+
writeln!(w, "pub struct {};", struct_name)?;
101+
writeln!(w)?;
102+
writeln!(w, "impl Pin for {} {{", struct_name)?;
103+
for reg in register_group.registers.iter() {
104+
let mut const_name = reg.name.clone();
105+
const_name.pop(); // Pop port character from register name (DDRB/PORTB/etc)..
106+
107+
writeln!(w, " /// {}.", reg.caption)?;
108+
writeln!(w, " type {} = {};", const_name, reg.name)?;
109+
}
110+
writeln!(w, " /// {}", signal.pad)?;
111+
writeln!(w, " const MASK: u8 = 1<<{};", idx)?;
112+
writeln!(w, "}}")?;
113+
writeln!(w)?;
114+
}
115+
}
116+
}
117+
Ok(())
118+
}
119+
120+
pub fn write_spi_modules(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> {
121+
if let Some(module) = mcu.module("SPI") {
122+
let peripheral = mcu.peripheral("SPI").expect("found SPI module but no peripheral");
123+
let port_peripheral = mcu.port_peripheral();
124+
125+
writeln!(w, "pub struct Spi;")?;
126+
writeln!(w)?;
127+
writeln!(w, "impl HardwareSpi for Spi {{")?;
128+
129+
for spi_signal in peripheral.signals() {
130+
let spi_signal_name = spi_signal.group.clone().expect("spi signal does not have group name");
131+
let (port_instance, port_signal) = port_peripheral.instance_signal_with_pad(&spi_signal.pad)
132+
.expect("no port signal associated with the spi signal pad");
133+
let pin_name = self::pin_name(port_instance, port_signal);
134+
135+
writeln!(w, " type {} = {};", spi_signal_name, pin_name)?;
136+
}
137+
138+
for reg in module.registers() {
139+
let const_name = match &reg.caption[..] {
140+
"SPI Data Register" => "SPDR",
141+
"SPI Status Register" => "SPSR",
142+
"SPI Control Register" => "SPCR",
143+
_ => panic!("unknown SPI module register: '{}'", reg.caption),
144+
};
145+
146+
147+
writeln!(w, " /// {}.", reg.caption)?;
148+
writeln!(w, " type {} = {};", const_name, reg.name)?;
149+
}
150+
writeln!(w, "}}")?;
151+
}
152+
Ok(())
153+
}
154+
155+
/// Gets the name of a pin.
156+
fn pin_name(instance: &Instance, signal: &Signal) -> String {
157+
let idx = signal.index.expect("signal with no index");
158+
format!("{}{}", instance.name, idx)
159+
}
160+
}
161+

build.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#! /bin/sh
2+
xargo build --target avr-atmega328p

src/cores/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.rs

src/cores/.gitkeep

Whitespace-only changes.

src/lib.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
//! Definitions of register addresses and bits within those registers
22
33
#![feature(asm)]
4-
#![feature(no_core)]
5-
#![no_core]
4+
#![feature(associated_consts)]
65

7-
extern crate core;
6+
#![no_std]
7+
8+
pub use self::reg::Register;
9+
pub use self::pin::Pin;
10+
pub use self::spi::HardwareSpi;
811

912
pub mod prelude;
13+
pub mod serial;
1014
pub mod timer0;
1115
pub mod timer1;
12-
pub mod serial;
16+
pub mod cores;
17+
18+
mod reg;
19+
mod pin;
20+
mod spi;
21+
22+
pub enum DataDirection {
23+
Input,
24+
Output,
25+
}
1326

1427
macro_rules! bit {
1528
(-, $pos:expr) => {};

src/pin.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use {DataDirection, Register};
2+
3+
/// An IO pin.
4+
pub trait Pin {
5+
/// The associated data direction registerr.
6+
type DDR: Register<u8>;
7+
/// The associated port register.
8+
type PORT: Register<u8>;
9+
/// The associated pin register.
10+
///
11+
/// Reads from the register will read input bits.
12+
/// Writes to the register will toggle bits.
13+
type PIN: Register<u8>;
14+
/// The mask of the pin used for accessing registers.
15+
const MASK: u8;
16+
17+
/// Sets the data direction of the pin.
18+
fn set_direction(direction: DataDirection) {
19+
match direction {
20+
DataDirection::Input => Self::set_input(),
21+
DataDirection::Output => Self::set_output(),
22+
}
23+
}
24+
25+
/// Sets the pin up as an input.
26+
fn set_input() {
27+
Self::DDR::unset(Self::MASK);
28+
}
29+
30+
/// Sets the pin up as an output.
31+
fn set_output() {
32+
Self::DDR::set(Self::MASK);
33+
}
34+
35+
/// Set the pin to high.
36+
///
37+
/// The pin must be configured as an output.
38+
fn set_high() {
39+
Self::PORT::set(Self::MASK);
40+
}
41+
42+
/// Set the pin to low.
43+
///
44+
/// The pin must be configured as an output.
45+
fn set_low() {
46+
Self::PORT::unset(Self::MASK);
47+
}
48+
49+
/// Toggles the pin.
50+
///
51+
/// The pin must be configured as an output.
52+
fn toggle() {
53+
// FIXME: We can optimise this on post-2006 AVRs.
54+
// http://www.avrfreaks.net/forum/toggle-state-output-pin
55+
// set(Self::PIN, Self::MASK);
56+
Self::PORT::toggle(Self::MASK);
57+
}
58+
59+
/// Check if the pin is currently high.
60+
///
61+
/// The pin must be configured as an input.
62+
fn is_high() -> bool {
63+
Self::PIN::is_set(Self::MASK)
64+
}
65+
66+
/// Checks if the pin is currently low.
67+
///
68+
/// The pin must be configured as an input.
69+
fn is_low() -> bool {
70+
Self::PIN::is_clear(Self::MASK)
71+
}
72+
}
73+

src/reg.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use core::{cmp, convert, ops};
2+
3+
pub trait RegVal : Copy + Clone +
4+
ops::BitAnd<Output=Self> +
5+
ops::BitAndAssign +
6+
ops::BitOr<Output=Self> +
7+
ops::BitOrAssign +
8+
ops::BitXor<Output=Self> +
9+
ops::BitXorAssign +
10+
ops::Not<Output=Self> +
11+
cmp::PartialEq + cmp::Eq +
12+
cmp::PartialOrd + cmp::Ord +
13+
convert::From<u8> {
14+
}
15+
16+
/// A register.
17+
pub trait Register<T: RegVal> {
18+
/// The address of the register.
19+
const ADDR: *mut T;
20+
21+
/// Writes a value to the register.
22+
fn write(value: T) {
23+
unsafe {
24+
*Self::ADDR = value;
25+
}
26+
}
27+
28+
/// Reads the value of the register.
29+
fn read() -> T {
30+
unsafe { *Self::ADDR }
31+
}
32+
33+
/// Sets a bitmask in a register.
34+
fn set(mask: T) {
35+
unsafe {
36+
*Self::ADDR |= mask;
37+
}
38+
}
39+
40+
/// Clears a bitmask from a register.
41+
fn unset(mask: T) {
42+
unsafe {
43+
*Self::ADDR &= !mask;
44+
}
45+
}
46+
47+
/// Toggles a mask in the register.
48+
fn toggle(mask: T) {
49+
unsafe {
50+
*Self::ADDR ^= mask;
51+
}
52+
}
53+
54+
/// Checks if a mask is set in the register.
55+
fn is_set(mask: T) -> bool {
56+
unsafe {
57+
(*Self::ADDR & mask) == mask
58+
}
59+
}
60+
61+
/// Checks if a mask is clear in the register.
62+
fn is_clear(mask: T) -> bool {
63+
unsafe {
64+
(*Self::ADDR & mask) == T::from(0)
65+
}
66+
}
67+
68+
/// Waits until some condition is true of the register.
69+
fn wait_until<F>(mut f: F)
70+
where F: FnMut() -> bool {
71+
loop {
72+
if f() {
73+
break;
74+
}
75+
}
76+
}
77+
78+
/// Waits until a mask is set.
79+
fn wait_until_set(mask: T) {
80+
Self::wait_until(|| Self::is_set(mask))
81+
}
82+
}
83+
84+
impl RegVal for u8 { }
85+
impl RegVal for u16 { }
86+

0 commit comments

Comments
 (0)