From 0449066ca28be801e10454af9f3202ce5a6c2bb0 Mon Sep 17 00:00:00 2001 From: Lionel Flandrin Date: Sun, 12 Oct 2014 22:42:39 +0200 Subject: [PATCH] Basic timers are now working. app_blink works! --- apps/app_blink_tm4c123gh6pm.rs | 27 +++- src/platformtree/platformtree.rs | 5 + src/zinc/hal/tm4c123gh6pm/mod.rs | 1 + src/zinc/hal/tm4c123gh6pm/pin.rs | 50 +++---- src/zinc/hal/tm4c123gh6pm/pin_pt.rs | 8 +- src/zinc/hal/tm4c123gh6pm/platformtree.rs | 4 +- src/zinc/hal/tm4c123gh6pm/sysctl.rs | 45 +++++- src/zinc/hal/tm4c123gh6pm/timer.rs | 160 ++++++++++++++++++++++ src/zinc/hal/tm4c123gh6pm/timer_pt.rs | 106 ++++++++++++++ 9 files changed, 363 insertions(+), 43 deletions(-) create mode 100644 src/zinc/hal/tm4c123gh6pm/timer.rs create mode 100644 src/zinc/hal/tm4c123gh6pm/timer_pt.rs diff --git a/apps/app_blink_tm4c123gh6pm.rs b/apps/app_blink_tm4c123gh6pm.rs index 0f92cc75..9cb9c40e 100644 --- a/apps/app_blink_tm4c123gh6pm.rs +++ b/apps/app_blink_tm4c123gh6pm.rs @@ -16,8 +16,18 @@ platformtree!( gpio { PortF { - led1@1 { direction = "out"; } - led2@2 { direction = "out"; } + led1@2 { direction = "out"; } + led2@3 { direction = "out"; } + } + } + + timer { + /* The mcu contain both 16/32bit and "wide" 32/64bit timers. */ + timer@w0 { + /* prescale sysclk to 1Mhz since the wait code expects 1us + * granularity */ + prescale = 16; + mode = "periodic"; } } } @@ -26,6 +36,7 @@ platformtree!( single_task { loop = "run"; args { + timer = &timer; led1 = &led1; led2 = &led2; } @@ -35,8 +46,18 @@ platformtree!( #[no_split_stack] pub fn run(args: &pt::run_args) { - use zinc::hal::pin::GPIO; + use zinc::hal::pin::GPIO; + use zinc::hal::timer::Timer; + loop { args.led1.set_high(); + args.led2.set_low(); + + args.timer.wait(1); + + args.led1.set_low(); args.led2.set_high(); + + args.timer.wait(1); + } } diff --git a/src/platformtree/platformtree.rs b/src/platformtree/platformtree.rs index 57a4ed9f..310d4328 100644 --- a/src/platformtree/platformtree.rs +++ b/src/platformtree/platformtree.rs @@ -17,9 +17,14 @@ #![experimental] #![feature(quote)] +#![feature(phase)] #![crate_name="platformtree"] #![crate_type="rlib"] +#[phase(plugin)] + +extern crate regex_macros; +extern crate regex; extern crate syntax; #[cfg(test)] extern crate hamcrest; diff --git a/src/zinc/hal/tm4c123gh6pm/mod.rs b/src/zinc/hal/tm4c123gh6pm/mod.rs index 58973b1e..a8471e5b 100644 --- a/src/zinc/hal/tm4c123gh6pm/mod.rs +++ b/src/zinc/hal/tm4c123gh6pm/mod.rs @@ -4,5 +4,6 @@ pub mod io; pub mod sysctl; pub mod pin; +pub mod timer; #[path="../../util/ioreg.rs"] mod util; diff --git a/src/zinc/hal/tm4c123gh6pm/pin.rs b/src/zinc/hal/tm4c123gh6pm/pin.rs index 20e878c8..477450be 100644 --- a/src/zinc/hal/tm4c123gh6pm/pin.rs +++ b/src/zinc/hal/tm4c123gh6pm/pin.rs @@ -7,7 +7,7 @@ use hal::tm4c123gh6pm::sysctl; use hal::tm4c123gh6pm::io::Reg; /// The pins are accessed through ports. Each port has 8 pins and are identified -/// by a letter (PortA, PortB, etc...) +/// by a letter (PortA, PortB, etc...). #[allow(missing_doc)] pub enum PortID { PortA, @@ -18,16 +18,10 @@ pub enum PortID { PortF, } -/// Structure describing a single GPIO port. Each port contains 8 pins. -struct Port { - /// Base address of the port - base: u32, -} - /// Structure describing a single HW pin pub struct Pin { - /// Port containing this pin - port: Port, + /// Base address for the port containing the pin + base: u32, /// Pin index in the port index: u8, } @@ -39,18 +33,18 @@ impl Pin { dir: GPIODirection) -> Pin { // Retrieve GPIO port peripheral to enable it - let (periph, port) = match pid { - PortA => (sysctl::periph::gpio::PORT_A, PORT_A), - PortB => (sysctl::periph::gpio::PORT_B, PORT_B), - PortC => (sysctl::periph::gpio::PORT_C, PORT_C), - PortD => (sysctl::periph::gpio::PORT_D, PORT_D), - PortE => (sysctl::periph::gpio::PORT_E, PORT_E), - PortF => (sysctl::periph::gpio::PORT_F, PORT_F), + let (periph, base) = match pid { + PortA => (sysctl::periph::gpio::PORT_A, PORT_A_BASE), + PortB => (sysctl::periph::gpio::PORT_B, PORT_B_BASE), + PortC => (sysctl::periph::gpio::PORT_C, PORT_C_BASE), + PortD => (sysctl::periph::gpio::PORT_D, PORT_D_BASE), + PortE => (sysctl::periph::gpio::PORT_E, PORT_E_BASE), + PortF => (sysctl::periph::gpio::PORT_F, PORT_F_BASE), }; periph.ensure_enabled(); - let pin = Pin { port: port, index: pin_index }; + let pin = Pin { base: base, index: pin_index }; pin.configure(dir); @@ -60,19 +54,19 @@ impl Pin { /// Configure GPIO pin fn configure(&self, dir: GPIODirection) { // Disable the GPIO during reconfig - let den = Reg::new(self.port.base + DEN); + let den = Reg::new(self.base + DEN); den.bitband_write(self.index, false); self.set_direction(dir); // Configure the "alternate function". 0 means GPIO, 1 means the port is // driven by another peripheral. - let afsel = Reg::new(self.port.base + AFSEL); + let afsel = Reg::new(self.base + AFSEL); afsel.bitband_write(self.index, false); // We can chose to drive each GPIO at either 2, 4 or 8mA. Default to 2mA for // now. - let drive = Reg::new(self.port.base + DR2R); + let drive = Reg::new(self.base + DR2R); drive.bitband_write(self.index, true); // XXX TODO: configure open drain/pull up/pull down/slew rate if necessary @@ -89,7 +83,7 @@ impl Pin { fn data_reg(&self) -> Reg { let off: u32 = 1u32 << ((self.index as uint) + 2); - Reg::new(self.port.base + DATA + off) + Reg::new(self.base + DATA + off) } } @@ -121,7 +115,7 @@ impl GPIO for Pin { /// Sets output GPIO direction. fn set_direction(&self, dir: GPIODirection) { - let reg = Reg::new(self.port.base + DIR); + let reg = Reg::new(self.base + DIR); reg.bitband_write(self.index, match dir { In => false, @@ -130,12 +124,12 @@ impl GPIO for Pin { } } -static PORT_A: Port = Port { base: 0x40004000 }; -static PORT_B: Port = Port { base: 0x40005000 }; -static PORT_C: Port = Port { base: 0x40006000 }; -static PORT_D: Port = Port { base: 0x40007000 }; -static PORT_E: Port = Port { base: 0x40024000 }; -static PORT_F: Port = Port { base: 0x40025000 }; +static PORT_A_BASE: u32 = 0x40004000; +static PORT_B_BASE: u32 = 0x40005000; +static PORT_C_BASE: u32 = 0x40006000; +static PORT_D_BASE: u32 = 0x40007000; +static PORT_E_BASE: u32 = 0x40024000; +static PORT_F_BASE: u32 = 0x40025000; // Register offsets from port base static DATA : u32 = 0x000; diff --git a/src/zinc/hal/tm4c123gh6pm/pin_pt.rs b/src/zinc/hal/tm4c123gh6pm/pin_pt.rs index 28f49ed4..02f8e0c7 100644 --- a/src/zinc/hal/tm4c123gh6pm/pin_pt.rs +++ b/src/zinc/hal/tm4c123gh6pm/pin_pt.rs @@ -24,12 +24,12 @@ pub fn verify(_: &mut Builder, cx: &mut ExtCtxt, node: Rc) { fn build_pin(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc) { let port_node = node.parent.clone().unwrap().upgrade().unwrap(); let ref port_path = port_node.path; + let port = TokenString(port_path.clone()); let error = | err: &str | { cx.parse_sess().span_diagnostic.span_err(port_node.path_span, err); }; - let port = TokenString(port_path.clone()); if node.name.is_none() { error("pin node must have a name"); @@ -41,7 +41,6 @@ fn build_pin(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc) { "out" => "zinc::hal::pin::Out", "in" => "zinc::hal::pin::In", bad => { - let attr = node.get_attr("direction"); error(format!("unknown direction `{}`, allowed values: `in`, `out`", bad).as_slice()); return; @@ -53,9 +52,8 @@ fn build_pin(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc) { let pin_str = match from_str::(node.path.as_slice()).unwrap() { 0 ...7 => &node.path, other => { - cx.parse_sess().span_diagnostic.span_err(node.path_span, - format!("unknown pin `{}`, allowed values: 0...7", - other).as_slice()); + error(format!("unknown pin `{}`, allowed values: 0...7", + other).as_slice()); return; } }; diff --git a/src/zinc/hal/tm4c123gh6pm/platformtree.rs b/src/zinc/hal/tm4c123gh6pm/platformtree.rs index 41e9a9dd..d15e9717 100644 --- a/src/zinc/hal/tm4c123gh6pm/platformtree.rs +++ b/src/zinc/hal/tm4c123gh6pm/platformtree.rs @@ -20,6 +20,7 @@ use builder::{Builder, add_node_dependency}; use node; mod pin_pt; +mod timer_pt; pub fn attach(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc) { node.materializer.set(Some(verify)); @@ -28,6 +29,7 @@ pub fn attach(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc) { match sub.path.as_slice() { "gpio" => pin_pt::attach(builder, cx, sub.clone()), + "timer" => timer_pt::attach(builder, cx, sub.clone()), _ => (), } } @@ -35,7 +37,7 @@ pub fn attach(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc) { fn verify(_: &mut Builder, cx: &mut ExtCtxt, node: Rc) { node.expect_no_attributes(cx); - node.expect_subnodes(cx, ["clock", "gpio"]); + node.expect_subnodes(cx, ["clock", "gpio", "timer"]); } pub fn add_node_dependency_on_clock(builder: &mut Builder, diff --git a/src/zinc/hal/tm4c123gh6pm/sysctl.rs b/src/zinc/hal/tm4c123gh6pm/sysctl.rs index 17b23d3c..f182a890 100644 --- a/src/zinc/hal/tm4c123gh6pm/sysctl.rs +++ b/src/zinc/hal/tm4c123gh6pm/sysctl.rs @@ -1,17 +1,11 @@ //! Low level system control (PLL, clock gating, ...) -/// SysCtl base address -static BASE: u32 = 0x400FE000; - pub mod periph { //! peripheral system control use core::iter::range; use hal::tm4c123gh6pm::io::Reg; - /// Run mode clock gating control offset - static RMCGC_OFFSET: u32 = 0x600; - /// Sysctl can reset/clock gate each module, as well as set various sleep and /// deep-sleep mode behaviour. pub struct Periph { @@ -71,4 +65,43 @@ pub mod periph { pub static PORT_F: super::Periph = super::Periph { class: CLASS, id: 5 }; } + pub mod timer { + //! Timer system control peripherals. Each timer has two independent + //! counters (A and B). + + static TIMER_CLASS: u8 = 0x4; + static TIMER_W_CLASS: u8 = 0x5c; + + pub static TIMER_0: super::Periph = super::Periph { class: TIMER_CLASS, + id: 0 }; + pub static TIMER_1: super::Periph = super::Periph { class: TIMER_CLASS, + id: 1 }; + pub static TIMER_2: super::Periph = super::Periph { class: TIMER_CLASS, + id: 2 }; + pub static TIMER_3: super::Periph = super::Periph { class: TIMER_CLASS, + id: 3 }; + pub static TIMER_4: super::Periph = super::Periph { class: TIMER_CLASS, + id: 4 }; + pub static TIMER_5: super::Periph = super::Periph { class: TIMER_CLASS, + id: 5 }; + + pub static TIMER_W_0: super::Periph = super::Periph { class: TIMER_W_CLASS, + id: 0 }; + pub static TIMER_W_1: super::Periph = super::Periph { class: TIMER_W_CLASS, + id: 1 }; + pub static TIMER_W_2: super::Periph = super::Periph { class: TIMER_W_CLASS, + id: 2 }; + pub static TIMER_W_3: super::Periph = super::Periph { class: TIMER_W_CLASS, + id: 3 }; + pub static TIMER_W_4: super::Periph = super::Periph { class: TIMER_W_CLASS, + id: 4 }; + pub static TIMER_W_5: super::Periph = super::Periph { class: TIMER_W_CLASS, + id: 5 }; + } + + /// Run mode clock gating control offset + static RMCGC_OFFSET: u32 = 0x600; } + +/// SysCtl base address +static BASE: u32 = 0x400FE000; diff --git a/src/zinc/hal/tm4c123gh6pm/timer.rs b/src/zinc/hal/tm4c123gh6pm/timer.rs new file mode 100644 index 00000000..acab6858 --- /dev/null +++ b/src/zinc/hal/tm4c123gh6pm/timer.rs @@ -0,0 +1,160 @@ +//! Timer configuration +//! This code should support both standand and wide timers + +use hal::tm4c123gh6pm::sysctl; +use hal::tm4c123gh6pm::io::Reg; +use hal::timer; + +/// There are 6 standard 16/32bit timers and 6 "wide" 32/64bit timers +#[allow(missing_doc)] +pub enum TimerId { + Timer0, + Timer1, + Timer2, + Timer3, + Timer4, + Timer5, + TimerW0, + TimerW1, + TimerW2, + TimerW3, + TimerW4, + TimerW5, +} + +/// Timer modes +pub enum Mode { + /// Periodic timer loops and restarts once the timeout is reached. + Periodic, + /// One shot timer is disabled once the timeout is reached. + OneShot, + /// RTC timer is based on the 32.768KHz clock and ticks at 1Hz + RTC, + /// EdgeCount timer counts rising/falling/both edge events on an + /// external pin. + EdgeCount, + /// EdgeTime timer measures the time it takes for a rising/falling/both edge + /// event to occur. + EdgeTime, + /// PWM mode can be used to generate a configurable square wave (frequence and + /// duty cycle) + PWM, +} + +/// Structure describing a single timer counter (both 16/32bit and 32/64bit) +pub struct Timer { + /// Base address for this counter + base : u32, + /// True if the counter is wide 32/64bit + wide : bool, + /// Current timer mode + mode : Mode, +} + +impl Timer { + /// Create and configure a Timer + pub fn new(tid: TimerId, + mode: Mode, + prescale: u16) -> Timer { + let (periph, base, wide) = match tid { + Timer0 => (sysctl::periph::timer::TIMER_0, TIMER_0_BASE, false), + Timer1 => (sysctl::periph::timer::TIMER_1, TIMER_1_BASE, false), + Timer2 => (sysctl::periph::timer::TIMER_2, TIMER_2_BASE, false), + Timer3 => (sysctl::periph::timer::TIMER_3, TIMER_3_BASE, false), + Timer4 => (sysctl::periph::timer::TIMER_4, TIMER_4_BASE, false), + Timer5 => (sysctl::periph::timer::TIMER_5, TIMER_5_BASE, false), + TimerW0 => (sysctl::periph::timer::TIMER_W_0, TIMER_W_0_BASE, true), + TimerW1 => (sysctl::periph::timer::TIMER_W_1, TIMER_W_1_BASE, true), + TimerW2 => (sysctl::periph::timer::TIMER_W_2, TIMER_W_2_BASE, true), + TimerW3 => (sysctl::periph::timer::TIMER_W_3, TIMER_W_3_BASE, true), + TimerW4 => (sysctl::periph::timer::TIMER_W_4, TIMER_W_4_BASE, true), + TimerW5 => (sysctl::periph::timer::TIMER_W_5, TIMER_W_5_BASE, true), + }; + + periph.ensure_enabled(); + + let timer = Timer { base: base, wide: wide, mode: mode}; + + timer.configure(prescale); + + timer + } + + /// Configure timer registers + /// XXX Only Periodic and OneShot modes are implemented so far + pub fn configure(&self, prescale: u16) { + + let ctl = Reg::new(self.base + CTL); + + // Make sure the timer is disabled before making changes. + ctl.bitband_write(0, false); + + let cfg = Reg::new(self.base + CFG); + + // Configure the timer as half-width so that we can use the prescaler + cfg.write32(0x4); + + let amr = Reg::new(self.base + AMR); + + // Configure TAMR + let mut amr_val = match self.mode { + OneShot => 0x1, + Periodic => 0x2, + _ => { return; /* Not implemented! */ }, + }; + + // We need to count down in order for the prescaler to work as a + // prescaler. If we count up it becomes a timer extension (i.e. it becomes + // the MSBs of the counter). + amr_val |= 0 << 4; + + amr.write32(amr_val); + + // Set maximum timeout value to overflow as late as possible + let tailr = Reg::new(self.base + TAILR); + + tailr.write32(0xffffffff); + + // Set prescale value + let apr = Reg::new(self.base + APR); + + apr.write32(prescale as u32); + + // Timer is now configured, we can enable it + ctl.bitband_write(0, true); + } +} + +impl timer::Timer for Timer { + /// Retrieve the current timer value + #[inline(always)] + fn get_counter(&self) -> u32 { + let tav = Reg::new(self.base + TAV); + + // We count down, however the trait code expects that the counter increases, + // so we just complement the value to get an increasing counter. + !tav.read32() + } +} + +static TIMER_0_BASE: u32 = 0x40030000; +static TIMER_1_BASE: u32 = 0x40031000; +static TIMER_2_BASE: u32 = 0x40032000; +static TIMER_3_BASE: u32 = 0x40033000; +static TIMER_4_BASE: u32 = 0x40034000; +static TIMER_5_BASE: u32 = 0x40035000; + +static TIMER_W_0_BASE: u32 = 0x40036000; +static TIMER_W_1_BASE: u32 = 0x40037000; +static TIMER_W_2_BASE: u32 = 0x4003C000; +static TIMER_W_3_BASE: u32 = 0x4003D000; +static TIMER_W_4_BASE: u32 = 0x4003E000; +static TIMER_W_5_BASE: u32 = 0x4003F000; + +// Register offsets from timer base +static CFG : u32 = 0x000; +static AMR : u32 = 0x004; +static CTL : u32 = 0x00C; +static TAILR : u32 = 0x028; +static APR : u32 = 0x038; +static TAV : u32 = 0x050; diff --git a/src/zinc/hal/tm4c123gh6pm/timer_pt.rs b/src/zinc/hal/tm4c123gh6pm/timer_pt.rs new file mode 100644 index 00000000..7913bab4 --- /dev/null +++ b/src/zinc/hal/tm4c123gh6pm/timer_pt.rs @@ -0,0 +1,106 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Lionel Flandrin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::rc::Rc; +use syntax::ext::base::ExtCtxt; + +use builder::{Builder, TokenString, add_node_dependency}; +use node; + +pub fn attach(builder: &mut Builder, _: &mut ExtCtxt, node: Rc) { + node.materializer.set(Some(verify)); + for timer_node in node.subnodes().iter() { + timer_node.materializer.set(Some(build_timer)); + add_node_dependency(&node, timer_node); + super::add_node_dependency_on_clock(builder, timer_node); + } +} + +pub fn verify(_: &mut Builder, cx: &mut ExtCtxt, node: Rc) { + node.expect_no_attributes(cx); +} + +fn build_timer(builder: &mut Builder, cx: &mut ExtCtxt, node: Rc) { + + let error = | err: &str | { + cx.parse_sess().span_diagnostic.span_err(node.path_span, err); + }; + + if !node.expect_attributes(cx, [ + ("prescale", node::IntAttribute), + ("mode", node::StrAttribute)]) { + return + } + + if node.name.is_none() { + cx.parse_sess().span_diagnostic.span_err(node.name_span, + "timer node must have a name"); + return + } + + let name = TokenString(node.name.clone().unwrap()); + let prescale = node.get_int_attr("prescale").unwrap() as u16; + let mode = node.get_string_attr("mode").unwrap(); + + // Timer path is in the form "w?[0-5][A-B]": + // - 'w' denotes a wide (32/64bit) counter. + // - The number is the timer ID. + // - The letter says which counter to use within that timer (each timer has + // two counters, A and B which can be configured independantly. + + let (wide_timer, id) = + match regex!(r"(w?)([0-5])").captures(node.path.as_slice()) { + Some(c) => { + (c.at(1) != "", c.at(2)) + } + None => { + error( + format!("invalid timer index `{}`, it should match `w?[0-5]`", + node.path).as_slice()); + return; + } + }; + + let mode = TokenString( + format!("zinc::hal::tm4c123gh6pm::timer::{}", + match mode.as_slice() { + "periodic" => "Periodic", + "one-shot" => "OneShot", + "RTC" => "RTC", + "edge-count" => "EdgeCount", + "edge-time" => "EdgeTime", + "PWM" => "PWM", + _ => { + error(format!("unknown mode {}, expected one of \ + periodic, one-shot, RTC, edge-count, edge-time \ + or PWM", mode).as_slice()); + return; + }})); + + let timer_name = TokenString(format!("zinc::hal::tm4c123gh6pm::timer::{}{}", + if wide_timer { + "TimerW" + } else { + "Timer" + }, id)); + + node.set_type_name("zinc::hal::tm4c123gh6pm::timer::Timer".to_string()); + + let st = quote_stmt!(&*cx, + let $name = zinc::hal::tm4c123gh6pm::timer::Timer::new( + $timer_name, $mode, $prescale); + ); + builder.add_main_statement(st); +}