Skip to content

WIP: Support for autogenerated mcu cores #4

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

Merged
merged 38 commits into from
Nov 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a831e48
Revert "Expose some structs for the IO ports"
dylanmckay Nov 5, 2018
d833813
Support auto-generated cores
dylanmckay Aug 29, 2017
394e07e
Hardware usart support
dylanmckay Aug 30, 2017
61b3214
Improve the names of the Spi trait consts
dylanmckay Aug 30, 2017
fcca0f8
Place ports into their own module
dylanmckay Aug 30, 2017
f175678
Make a module public
dylanmckay Aug 30, 2017
a19ef84
Fix typo
dylanmckay Sep 23, 2017
72b0169
Rename RegVal to Register
dylanmckay Sep 23, 2017
1888fa5
Propagate arguments to build script
dylanmckay Sep 23, 2017
5d65ad4
Add a few stubs required from std into the library
dylanmckay Sep 23, 2017
190a3ec
Add a cores::current module for getting the current microcontroller
dylanmckay Sep 23, 2017
906b548
Do more work
dylanmckay Nov 17, 2017
468289e
Support raw things
dylanmckay Dec 13, 2017
1f638db
Add a timer trait
dylanmckay Dec 13, 2017
274d461
More work done
dylanmckay Dec 13, 2017
d125f69
Get 16-bit timers working
dylanmckay Dec 13, 2017
0157a85
Implement the Timer16 trait
dylanmckay Dec 13, 2017
cb6e0d3
Invert the direction of the Register<T> type parameter
dylanmckay Dec 13, 2017
ea1aef9
Remove as much atmega328p-hardcoded stuff as possible
dylanmckay Dec 13, 2017
c5dac9d
Move the serial module into a 'legacy' module
dylanmckay Dec 13, 2017
3e29e87
Rename ADDR to ADDRESS
dylanmckay Dec 13, 2017
d3d41e4
Silence the half-written module
dylanmckay Dec 13, 2017
c5946dc
Remove all compiler warnings
dylanmckay Dec 13, 2017
76298ab
Add myself to authors
dylanmckay Dec 13, 2017
e1a05bb
Move the build script into a subdir
dylanmckay Dec 13, 2017
3f3c8e2
Move 'mod' to more appropriate place
dylanmckay Dec 13, 2017
c486e1b
Rename crate to 'ruduino'
dylanmckay Dec 16, 2017
61c62ad
Merge Mask and Bitset, remove Bitset
dylanmckay Dec 17, 2017
2b58e36
Add documentation
dylanmckay Dec 17, 2017
2430724
Remove temporarily-added debugging files
dylanmckay Dec 17, 2017
dcab721
Use the correct timer thing
dylanmckay Dec 17, 2017
4b45163
Rename AVR_FREQUENCY to AVR_FREQUENCY_HZ
dylanmckay Nov 5, 2018
f94b23e
Use the new panic handler feature
dylanmckay Nov 5, 2018
8a320ca
Assume atmega328 when building documentation
dylanmckay Nov 5, 2018
f90d5b2
Clean up the documentation and public exports
dylanmckay Nov 5, 2018
aecd4ed
Rename Mask to RegisterBits
dylanmckay Nov 5, 2018
3fbe335
Fixes
dylanmckay Nov 5, 2018
49a34da
Fixes from incorrect rebase
dylanmckay Nov 5, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@ version = "0.1.1"
authors = [
"The AVR-Rust Project Developers",
"Jake Goulding <jake.goulding@gmail.com>",
"Dylan McKay <me@dylanmckay.io>",
]

license = "MIT/Apache-2.0"
readme = "README.md"
repository = "https://github.com/avr-rust/ruduino"
description = """
Reusable components for the Arduino Uno.
Reusable components for AVR microcontrollers
"""

build = "core_generator/build.rs"

keywords = ["avr", "arduino", "uno"]

[build-dependencies]
avr-mcu = "0.2"

97 changes: 97 additions & 0 deletions core_generator/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
extern crate avr_mcu;

mod gen;

use avr_mcu::*;
use std::fs::{self, File};
use std::io;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

/// The MCU that will be assumed when running 'cargo doc' targeting
/// archicectures that are not AVR.
const DEFAULT_MCU_FOR_NON_AVR_DOCS: &'static str = "atmega328";

fn src_path() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("src")
}

fn cores_path() -> PathBuf {
src_path().join("cores")
}

fn core_module_name(mcu: &Mcu) -> String {
mcu.device.name.to_lowercase().to_owned()
}

fn main() {
if !cores_path().exists() {
fs::create_dir_all(&cores_path()).expect("could not create cores directory");
}

let current_mcu = if cfg!(arch = "avr") {
avr_mcu::current::mcu()
.expect("no target cpu specified")
} else {
avr_mcu::microcontroller(DEFAULT_MCU_FOR_NON_AVR_DOCS)
};

generate_config_module().unwrap();
generate_cores(&[current_mcu]).unwrap();
}

fn generate_cores(mcus: &[Mcu]) -> Result<(), io::Error> {
for mcu in mcus {
generate_core_module(mcu).expect("failed to generate mcu core");
}
generate_cores_mod_rs(mcus)
}

fn generate_config_module() -> Result<(), io::Error> {
let path = src_path().join("config.rs");
let mut f = File::create(&path)?;

let clock = env!("AVR_CPU_FREQUENCY_HZ");
writeln!(f, "/// The clock frequency of device being targeted in Hertz.")?;
writeln!(f, "pub const CPU_FREQUENCY_HZ: u32 = {};", clock)?;
Ok(())
}

fn generate_core_module(mcu: &Mcu) -> Result<(), io::Error> {
let path = cores_path().join(format!("{}.rs", core_module_name(mcu)));
let mut file = File::create(&path)?;
write_core_module(mcu, &mut file)
}

fn generate_cores_mod_rs(mcus: &[Mcu]) -> Result<(), io::Error> {
let path = cores_path().join("mod.rs");
let mut w = File::create(&path)?;

writeln!(w)?;
for mcu in mcus {
let module_name = core_module_name(mcu);
writeln!(w, "/// The {}.", mcu.device.name)?;
writeln!(w, "pub mod {};", module_name)?;

writeln!(w, "#[cfg(all(target_arch = \"avr\", target_cpu = \"{}\"))]", module_name)?;
writeln!(w, "pub use self::{} as current;", module_name)?;
}
writeln!(w)
}

fn write_core_module(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> {
writeln!(w, "//! Core for {}.", mcu.device.name)?;
writeln!(w)?;
writeln!(w, "use {{RegisterBits, Register}};")?;
writeln!(w, "use modules;")?;
writeln!(w)?;

gen::write_registers(mcu, w)?;
gen::write_pins(mcu, w)?;
gen::write_spi_modules(mcu, w)?;
gen::write_usarts(mcu, w)?;
gen::write_timers(mcu, w)?;

writeln!(w)
}

240 changes: 240 additions & 0 deletions core_generator/gen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
use avr_mcu::*;
use std::io;
use std::io::prelude::*;

pub fn write_registers(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> {
for register in mcu.registers() {
let ty = if register.size == 1 { "u8" } else { "u16" };

// HACK: Skip, atmeg328p pack defines two of these.
if register.name == "GTCCR" { continue; }

writeln!(w, "pub struct {};", register.name)?;
writeln!(w)?;

writeln!(w, "impl {} {{", register.name)?;
for bitfield in register.bitfields.iter() {
// Create a mask for the whole bitset.
writeln!(w, " pub const {}: RegisterBits<Self> = RegisterBits::new(0x{:x});", bitfield.name, bitfield.mask)?;

// We create masks for the individual bits in the field if there
// is more than one bit in the field.
let mut current_mask = bitfield.mask;
let mut current_mask_bit_num = 0;
for current_register_bit_num in 0..15 {
if (current_mask & 0b1) == 0b1 {
writeln!(w, " pub const {}{}: RegisterBits<Self> = RegisterBits::new(1<<{});",
bitfield.name, current_mask_bit_num, current_register_bit_num)?;
current_mask_bit_num += 1;
}

current_mask >>= 1;
}
writeln!(w)?;
}
writeln!(w, "}}")?;
writeln!(w)?;

writeln!(w, "impl Register for {} {{", register.name)?;
writeln!(w, " type T = {};", ty)?;
writeln!(w, " const ADDRESS: *mut {} = 0x{:x} as *mut {};", ty, register.offset, ty)?;
writeln!(w, "}}")?;
}

Ok(())
}

pub fn write_pins(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> {
if let Some(port) = mcu.peripheral("PORT") {
writeln!(w, "pub mod port {{")?;
writeln!(w, " use super::*;")?;
writeln!(w, " use Pin;")?;
writeln!(w)?;

for instance in port.instances.iter() {
let port_letter = instance.name.chars().rev().next().unwrap();

for signal in instance.signals.iter() {
let idx = signal.index.expect("signal with no index");
let struct_name = format!("{}{}", port_letter, idx);

let io_module = mcu.modules.iter().find(|m| m.name == "PORT")
.expect("no port io module defined for this port");
let register_group = io_module.register_groups.iter()
.find(|rg| rg.name == instance.name)
.expect("no register group defined for this port");

writeln!(w, " pub struct {};", struct_name)?;
writeln!(w)?;
writeln!(w, " impl Pin for {} {{", struct_name)?;
for reg in register_group.registers.iter() {
let mut const_name = reg.name.clone();
const_name.pop(); // Pop port character from register name (DDRB/PORTB/etc)..

writeln!(w, " /// {}.", reg.caption)?;
writeln!(w, " type {} = {};", const_name, reg.name)?;
}
writeln!(w, " /// {}", signal.pad)?;
writeln!(w, " const MASK: u8 = 1<<{};", idx)?;
writeln!(w, " }}")?;
writeln!(w)?;
}
}

writeln!(w, "}}")?;
writeln!(w)?;
}
Ok(())
}

pub fn write_spi_modules(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> {
if let Some(module) = mcu.module("SPI") {
let peripheral = mcu.peripheral("SPI").expect("found SPI module but no peripheral");
let port_peripheral = mcu.port_peripheral();

writeln!(w, "pub struct Spi;")?;
writeln!(w)?;
writeln!(w, "impl modules::HardwareSpi for Spi {{")?;

for spi_signal in peripheral.signals() {
let spi_signal_name = spi_signal.group.clone().expect("spi signal does not have group name");
let (port_instance, port_signal) = port_peripheral.instance_signal_with_pad(&spi_signal.pad)
.expect("no port signal associated with the spi signal pad");
let pin_name = self::pin_name(port_instance, port_signal);

let const_name = match &spi_signal_name[..] {
"MISO" => "MasterInSlaveOut",
"MOSI" => "MasterOutSlaveIn",
"SCK" => "Clock",
"SS" => "SlaveSelect",
_ => panic!("unknown spi signal name: '{}'", spi_signal_name),
};

writeln!(w, " type {} = {};", const_name, pin_name)?;
}

for reg in module.registers() {
let const_name = match &reg.caption[..] {
"SPI Data Register" => "DataRegister",
"SPI Status Register" => "StatusRegister",
"SPI Control Register" => "ControlRegister",
_ => panic!("unknown SPI module register: {}", reg.caption),
};

writeln!(w, " type {} = {};", const_name, reg.name)?;
}
writeln!(w, "}}")?;
writeln!(w)?;
}
Ok(())
}

pub fn write_usarts(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> {
if let Some(module) = mcu.module("USART") {
for usart in module.register_groups.iter() {
writeln!(w, "/// The {} module.", usart.name)?;
writeln!(w, "pub struct {};", usart.name)?;
writeln!(w)?;
writeln!(w, "impl modules::HardwareUsart for {} {{", usart.name)?;
for register in usart.registers.iter() {
let reg_ty = if register.name.starts_with("UDR") { // the data register.
"DataRegister".to_owned()
} else if register.name.starts_with("UCSR") { // one of the three control/status registers.
let suffix = register.name.chars().rev().next().unwrap();
format!("ControlRegister{}", suffix)
} else if register.name.starts_with("UBRR") { // the baud rate register.
"BaudRateRegister".to_owned()
} else {
panic!("unknown usart register '{}'", register.name);
};
writeln!(w, " type {} = {};", reg_ty, register.name)?;
}
writeln!(w, "}}")?;
writeln!(w)?;
}
}
Ok(())
}

pub fn write_timers(mcu: &Mcu, w: &mut Write) -> Result<(), io::Error> {
if let Some(tc) = mcu.module("TC8") { // Timer/Counter, 8-bit.
const TYPE_NAME: &'static str = "Timer8";

let find_reg = |name: &'static str| {
tc.registers().find(|r| r.name.starts_with(name))
.expect(&format!("could not find '{}' register", name))
};
let find_reg_suffix = |name: &'static str, suffix: &'static str| {
tc.registers().find(|r| r.name.starts_with(name) && r.name.ends_with(suffix))
.expect(&format!("could not find '{}' register", name))
};
let timer_number = find_reg("TIMSK").name.chars().last().unwrap()
.to_digit(10).unwrap();

writeln!(w, "/// 8-bit timer.")?;
writeln!(w, "pub struct {};", TYPE_NAME)?;
writeln!(w)?;
writeln!(w, "impl modules::Timer8 for {} {{", TYPE_NAME)?;
writeln!(w, " type CompareA = {};", find_reg_suffix("OCR", "A").name)?;
writeln!(w, " type CompareB = {};", find_reg_suffix("OCR", "B").name)?;
writeln!(w, " type Counter = {};", find_reg("TCNT").name)?;
writeln!(w, " type ControlA = {};", find_reg_suffix("TCCR", "A").name)?;
writeln!(w, " type ControlB = {};", find_reg_suffix("TCCR", "B").name)?;
writeln!(w, " type InterruptMask = {};", find_reg("TIMSK").name)?;
writeln!(w, " type InterruptFlag = {};", find_reg("TIFR").name)?;
writeln!(w, " const CS0: RegisterBits<Self::ControlB> = Self::ControlB::CS00;")?;
writeln!(w, " const CS1: RegisterBits<Self::ControlB> = Self::ControlB::CS01;")?;
writeln!(w, " const CS2: RegisterBits<Self::ControlB> = Self::ControlB::CS02;")?;
writeln!(w, " const WGM0: RegisterBits<Self::ControlA> = Self::ControlA::WGM00;")?;
writeln!(w, " const WGM1: RegisterBits<Self::ControlA> = Self::ControlA::WGM01;")?;
writeln!(w, " const WGM2: RegisterBits<Self::ControlB> = Self::ControlB::WGM020;")?;
writeln!(w, " const OCIEA: RegisterBits<Self::InterruptMask> = Self::InterruptMask::OCIE{}A;", timer_number)?;
writeln!(w, "}}")?;
}

if let Some(tc) = mcu.module("TC16") { // Timer/Counter, 16-bit.
const TYPE_NAME: &'static str = "Timer16";

let find_reg = |name: &'static str| {
tc.registers().find(|r| r.name.starts_with(name))
.expect(&format!("could not find '{}' register", name))
};
let find_reg_suffix = |name: &'static str, suffix: &'static str| {
tc.registers().find(|r| r.name.starts_with(name) && r.name.ends_with(suffix))
.expect(&format!("could not find '{}' register", name))
};
let timer_number = find_reg("TIMSK").name.chars().last().unwrap()
.to_digit(10).unwrap();

writeln!(w, "/// 16-bit timer.")?;
writeln!(w, "pub struct {};", TYPE_NAME)?;
writeln!(w)?;
writeln!(w, "impl modules::Timer16 for {} {{", TYPE_NAME)?;
writeln!(w, " type CompareA = {};", find_reg_suffix("OCR", "A").name)?;
writeln!(w, " type CompareB = {};", find_reg_suffix("OCR", "B").name)?;
writeln!(w, " type Counter = {};", find_reg("TCNT").name)?;
writeln!(w, " type ControlA = {};", find_reg_suffix("TCCR", "A").name)?;
writeln!(w, " type ControlB = {};", find_reg_suffix("TCCR", "B").name)?;
writeln!(w, " type ControlC = {};", find_reg_suffix("TCCR", "C").name)?;
writeln!(w, " type InterruptMask = {};", find_reg("TIMSK").name)?;
writeln!(w, " type InterruptFlag = {};", find_reg("TIFR").name)?;
writeln!(w, " const CS0: RegisterBits<Self::ControlB> = Self::ControlB::CS10;")?;
writeln!(w, " const CS1: RegisterBits<Self::ControlB> = Self::ControlB::CS11;")?;
writeln!(w, " const CS2: RegisterBits<Self::ControlB> = Self::ControlB::CS12;")?;
writeln!(w, " const WGM0: RegisterBits<Self::ControlA> = Self::ControlA::WGM10;")?;
writeln!(w, " const WGM1: RegisterBits<Self::ControlA> = Self::ControlA::WGM11;")?;
writeln!(w, " const WGM2: RegisterBits<Self::ControlB> = Self::ControlB::WGM10;")?;
writeln!(w, " const WGM3: RegisterBits<Self::ControlB> = Self::ControlB::WGM11;")?;
writeln!(w, " const OCIEA: RegisterBits<Self::InterruptMask> = Self::InterruptMask::OCIE{}A;", timer_number)?;
writeln!(w, "}}")?;
}

Ok(())
}

/// Gets the name of a pin.
fn pin_name(instance: &Instance, signal: &Signal) -> String {
let idx = signal.index.expect("signal with no index");
let letter = instance.name.chars().rev().next().unwrap();
format!("port::{}{}", letter, idx)
}
14 changes: 14 additions & 0 deletions examples/spi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#![no_std]
#![no_main]

extern crate arduino;
use arduino::cores::current;

// Some devices may have multiple SPI modules.
// The ATmega328p only has one.
type Spi = current::Spi;

#[no_mangle]
pub extern fn main() {
}

2 changes: 2 additions & 0 deletions src/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Generated automatically.
config.rs
1 change: 1 addition & 0 deletions src/cores/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.rs
Empty file added src/cores/.gitkeep
Empty file.
Loading