From 7665a23896d0558dd7d0e865a62dddc13acd7008 Mon Sep 17 00:00:00 2001 From: occheung Date: Mon, 7 Sep 2020 14:07:39 +0800 Subject: [PATCH] scpi: introduce master clock setup --- memory.x | 17 +----- src/lib.rs | 116 +++++++++++++++++--------------------- src/scpi.rs | 156 +++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 181 insertions(+), 108 deletions(-) diff --git a/memory.x b/memory.x index ae8742e..c15bee1 100644 --- a/memory.x +++ b/memory.x @@ -1,8 +1,7 @@ MEMORY { /* FLASH and RAM are mandatory memory regions */ - FLASH : ORIGIN = 0x08000000, LENGTH = 1024K - FLASH1 : ORIGIN = 0x08100000, LENGTH = 1024K + FLASH : ORIGIN = 0x08000000, LENGTH = 2M RAM : ORIGIN = 0x20000000, LENGTH = 128K /* AXISRAM */ @@ -11,7 +10,7 @@ MEMORY /* SRAM */ SRAM1 : ORIGIN = 0x30000000, LENGTH = 128K SRAM2 : ORIGIN = 0x30020000, LENGTH = 128K - SRAM3 : ORIGIN = 0x30040000, LENGTH = 32K + SRAM3 (rwx) : ORIGIN = 0x30040000, LENGTH = 32K SRAM4 : ORIGIN = 0x38000000, LENGTH = 64K /* Backup SRAM */ @@ -31,22 +30,10 @@ _stack_start = ORIGIN(RAM) + LENGTH(RAM); /* _stext = ORIGIN(FLASH) + 0x40c; */ SECTIONS { - .itcm : ALIGN(8) { - *(.itcm .itcm.*); - . = ALIGN(8); - } > ITCM .axisram : ALIGN(8) { *(.axisram .axisram.*); . = ALIGN(8); } > AXISRAM - .sram1 (NOLOAD) : ALIGN(4) { - *(.sram1 .sram1.*); - . = ALIGN(4); - } > SRAM1 - .sram2 (NOLOAD) : ALIGN(4) { - *(.sram2 .sram2.*); - . = ALIGN(4); - } > SRAM2 .sram3 (NOLOAD) : ALIGN(4) { *(.sram3 .sram3.*); . = ALIGN(4); diff --git a/src/lib.rs b/src/lib.rs index a1af640..2ace1b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,9 @@ use core::{ use cortex_m; use cortex_m_semihosting::hprintln; +#[macro_use] +extern crate mashup; + #[macro_use] pub mod bitmask_macro; @@ -56,6 +59,12 @@ pub enum Error { ParameterError, } +pub enum ClockSource { + OSC, + SMA, + MMCX, +} + /* * Struct for Urukul master device */ @@ -145,6 +154,8 @@ pub trait UrukulTraits { type Error; fn get_channel_switch_status(&mut self, channel: u32) -> Result; fn set_channel_switch(&mut self, channel: u32, status: bool) -> Result<(), Self::Error>; + fn set_clock_source(&mut self, source: ClockSource) -> Result<(), Self::Error>; + fn set_clock_division(&mut self, division: u8) -> Result<(), Self::Error>; } impl UrukulTraits for Urukul @@ -185,71 +196,42 @@ where } } - // fn switch_on(&mut self, channel: u32) -> Result<(), Self::Error>{ - // if channel < 16 { - // let switch_set = channel | u32::from(self.config_register.get_status(StatusMask::RF_SW)?); + fn set_clock_source(&mut self, source: ClockSource) -> Result<(), Self::Error> { + let result = match source { + ClockSource::OSC => self.config_register.set_configurations(&mut [ + (CFGMask::CLK_SEL0, 0), + (CFGMask::CLK_SEL1, 0), + ]), + ClockSource::MMCX => self.config_register.set_configurations(&mut [ + (CFGMask::CLK_SEL0, 0), + (CFGMask::CLK_SEL1, 1), + ]), + ClockSource::SMA => self.config_register.set_configurations(&mut [ + (CFGMask::CLK_SEL0, 1), + ]), + }; + match result { + Ok(_) => Ok(()), + Err(_e) => Err(_e), + } + } - // match self.config_register.set_configurations(&mut [ - // (CFGMask::RF_SW, switch_set), - // ]) { - // Ok(_) => Ok(()), - // Err(_e) => Err(_e), - // } - // } else { - // Err(Error::ParameterError) - // } - // } -} - -// /* -// * Struct for a better Urukul master device -// */ -// pub struct BetterUrukul<'a, SPI, CS0, CS1, CS2, GPIO> { -// cpld: CPLD, -// parts: Option, SPI, CS0, CS1, CS2, GPIO>>, -// config_register: Option, SPI, CS0, CS1, CS2, GPIO>>>, -// attenuator: Option, SPI, CS0, CS1, CS2, GPIO>>>, -// dds: [Option, SPI, CS0, CS1, CS2, GPIO>>>; 4], -// } - -// impl<'a, SPI, CS0, CS1, CS2, GPIO> BetterUrukul<'a, SPI, CS0, CS1, CS2, GPIO> -// where -// SPI: Transfer, -// CS0: OutputPin, -// CS1: OutputPin, -// CS2: OutputPin, -// GPIO: OutputPin, -// { -// pub fn new(spi: SPI, chip_select: (CS0, CS1, CS2), io_update: GPIO) -> Self { -// // let switch = CPLD::new(spi, chip_select, io_update); -// // let parts = switch.split(); - -// // Construct Urukul -// BetterUrukul { -// cpld: CPLD::new(spi, chip_select, io_update), -// // parts: CPLD::new(spi, chip_select, io_update).split(), -// // config_register: ConfigRegister::new(self.parts.spi1), -// // attenuator: Attenuator::new(self.parts.spi2), -// // dds: [ -// // DDS::new(self.parts.spi4, f_ref_clks[1]), -// // DDS::new(self.parts.spi5, f_ref_clks[1]), -// // DDS::new(self.parts.spi6, f_ref_clks[2]), -// // DDS::new(self.parts.spi7, f_ref_clks[3]), -// // ], -// parts: None, -// config_register: None, -// attenuator: None, -// dds: [None, None, None, None], -// } -// } - -// pub fn init(&'a mut self, f_ref_clks:[u64; 4]) { -// self.parts = Some(self.cpld.split()); -// self.config_register = Some(ConfigRegister::new(self.parts.unwrap().spi1)); -// self.attenuator = Some(Attenuator::new(self.parts.unwrap().spi2)); -// self.dds[0] = Some(DDS::new(self.parts.unwrap().spi4, f_ref_clks[0])); -// self.dds[1] = Some(DDS::new(self.parts.unwrap().spi5, f_ref_clks[1])); -// self.dds[2] = Some(DDS::new(self.parts.unwrap().spi6, f_ref_clks[2])); -// self.dds[3] = Some(DDS::new(self.parts.unwrap().spi7, f_ref_clks[3])); -// } -// } \ No newline at end of file + fn set_clock_division(&mut self, division: u8) -> Result<(), Self::Error> { + let result = match division { + 1 => self.config_register.set_configurations(&mut [ + (CFGMask::DIV, 1), + ]), + 2 => self.config_register.set_configurations(&mut [ + (CFGMask::DIV, 2), + ]), + 4 => self.config_register.set_configurations(&mut [ + (CFGMask::DIV, 3), + ]), + _ => Err(Error::ParameterError), + }; + match result { + Ok(_) => Ok(()), + Err(_e) => Err(_e), + } + } +} \ No newline at end of file diff --git a/src/scpi.rs b/src/scpi.rs index 672cada..698634b 100644 --- a/src/scpi.rs +++ b/src/scpi.rs @@ -7,6 +7,7 @@ use scpi::NumericValues; use core::convert::{TryFrom, TryInto}; use core::str; +use core::str::Utf8Error; use scpi::ieee488::commands::*; use scpi::scpi::commands::*; use scpi::{ @@ -31,7 +32,12 @@ use scpi::{ use embedded_hal::blocking::spi::Transfer; use cortex_m_semihosting::hprintln; -use crate::{Urukul, UrukulTraits, Error as UrukulError}; +use crate::{ + Urukul, + UrukulTraits, + Error as UrukulError, + ClockSource, +}; #[macro_export] macro_rules! recursive_scpi_tree { @@ -56,7 +62,7 @@ macro_rules! recursive_scpi_tree { }; // Handle optional header with sub-commands - ([$header_name: expr] => {$($($rest: tt)=>*);*}) => { + ([$header_name: expr] => {$($($rest: tt)=>*),*}) => { Node { name: str::as_bytes($header_name), handler: None, @@ -70,7 +76,7 @@ macro_rules! recursive_scpi_tree { }; // Handle non-optional header with sub-commands - ($header_name: expr => {$($($rest: tt)=>*);*}) => { + ($header_name: expr => {$($($rest: tt)=>*),*}) => { Node { name: str::as_bytes($header_name), handler: None, @@ -86,7 +92,7 @@ macro_rules! recursive_scpi_tree { #[macro_export] macro_rules! scpi_root { - ($($($node: tt)=>*);*) => { + ($($($node: tt)=>*),*) => { &Node { name: b"ROOT", optional: false, @@ -131,31 +137,34 @@ impl Command for HelloWorldCommand { } } -macro_rules! declare_channel_commands { - ($($header: ident),*) => { - mashup! { - $( - m["channel0" $header] = Channel0 $header Command; - m["channel1" $header] = Channel1 $header Command; - m["channel2" $header] = Channel2 $header Command; - m["channel3" $header] = Channel3 $header Command; - )* - } +pub struct Channel0SwitchCommand {} +pub struct Channel1SwitchCommand {} +pub struct Channel2SwitchCommand {} +pub struct Channel3SwitchCommand {} +pub struct ClockSourceCommand {} +pub struct ClockDivisionCommand {} - m! { - $( - pub struct "channel0" $header {} - pub struct "channel1" $header {} - pub struct "channel2" $header {} - pub struct "channel3" $header {} - )* +impl Command for Channel0SwitchCommand { + nquery!(); + + fn event(&self, context: &mut Context, args: &mut Tokenizer) -> Result<()> { + match context.device.get_channel_switch_status(0) { + Ok(status) => { + let next_state: bool = match args.next_data(true) { + Ok(Some(token)) => token.try_into()?, + Ok(None) => !status, + Err(_) => return Err(ErrorCode::IllegalParameterValue.into()), + }; + match context.device.set_channel_switch(0, next_state) { + Ok(()) => Ok(()), + Err(_) => Err(Error::new(ErrorCode::HardwareError)), + } + }, + Err(_) => Err(Error::new(ErrorCode::HardwareError)), } - }; + } } -declare_channel_commands!(Switch); - -// pub struct Channel1SwitchCommand {} impl Command for Channel1SwitchCommand { nquery!(); @@ -169,7 +178,6 @@ impl Command for Channel1SwitchCommand { }; match context.device.set_channel_switch(1, next_state) { Ok(()) => Ok(()), - Err(T) => panic!("Switched an illegal channel!"), Err(_) => Err(Error::new(ErrorCode::HardwareError)), } }, @@ -178,6 +186,102 @@ impl Command for Channel1SwitchCommand { } } +impl Command for Channel2SwitchCommand { + nquery!(); + + fn event(&self, context: &mut Context, args: &mut Tokenizer) -> Result<()> { + match context.device.get_channel_switch_status(2) { + Ok(status) => { + let next_state: bool = match args.next_data(true) { + Ok(Some(token)) => token.try_into()?, + Ok(None) => !status, + Err(_) => return Err(ErrorCode::IllegalParameterValue.into()), + }; + match context.device.set_channel_switch(2, next_state) { + Ok(()) => Ok(()), + Err(_) => Err(Error::new(ErrorCode::HardwareError)), + } + }, + Err(_) => Err(Error::new(ErrorCode::HardwareError)), + } + } +} + +impl Command for Channel3SwitchCommand { + nquery!(); + + fn event(&self, context: &mut Context, args: &mut Tokenizer) -> Result<()> { + match context.device.get_channel_switch_status(3) { + Ok(status) => { + let next_state: bool = match args.next_data(true) { + Ok(Some(token)) => token.try_into()?, + Ok(None) => !status, + Err(_) => return Err(ErrorCode::IllegalParameterValue.into()), + }; + match context.device.set_channel_switch(3, next_state) { + Ok(()) => Ok(()), + Err(_) => Err(Error::new(ErrorCode::HardwareError)), + } + }, + Err(_) => Err(Error::new(ErrorCode::HardwareError)), + } + } +} + +impl Command for ClockSourceCommand { + nquery!(); + + fn event(&self, context: &mut Context, args: &mut Tokenizer) -> Result<()> { + let data: &[u8] = match args.next_data(false)? { + Some(Token::CharacterProgramData(s)) => s, + _ => return Err(ErrorCode::IllegalParameterValue.into()), + }; + let result = match str::from_utf8(data) { + Ok(str_param) => match str_param { + "OSC" => context.device.set_clock_source(ClockSource::OSC), + "MMCX" => context.device.set_clock_source(ClockSource::MMCX), + "SMA" => context.device.set_clock_source(ClockSource::SMA), + _ => return Err(ErrorCode::IllegalParameterValue.into()), + } + _ => return Err(ErrorCode::IllegalParameterValue.into()), + }; + match result { + Ok(_) => Ok(()), + Err(_) => Err(Error::new(ErrorCode::HardwareError)), + } + } +} + +impl Command for ClockDivisionCommand { + nquery!(); + + fn event(&self, context: &mut Context, args: &mut Tokenizer) -> Result<()> { + let data :u8 = match args.next_data(false)? { + Some(token) => { + match f32::try_from(token) { + Ok(val) => { + hprintln!("{}", val).unwrap(); + if val == 1.0 || val == 2.0 || val == 4.0 { + val as u8 + } else { + return Err(ErrorCode::IllegalParameterValue.into()) + } + }, + Err(_e) => { + hprintln!("Checked numberic error").unwrap(); + return Err(ErrorCode::IllegalParameterValue.into()) + }, + } + }, + _ => return Err(ErrorCode::IllegalParameterValue.into()), + }; + match context.device.set_clock_division(data) { + Ok(()) => Ok(()), + Err(_) => Err(Error::new(ErrorCode::HardwareError)), + } + } +} + /* * Implement "Device" trait from SCPI * TODO: Implement mandatory commands