use quick_xml::events::Event; use quick_xml::Reader; use std::io::{BufRead, BufReader, Read, Write}; #[derive(Debug)] pub enum ParseError { UnexpectedTag, MissingValue, ParseIntError, NonUTF8, } #[derive(Default, Debug)] struct Field { name: String, lsb: usize, msb: usize, } #[derive(Default, Debug)] struct Register { name: String, offset: usize, description: Option, fields: Vec, } #[derive(Default, Debug)] struct Peripheral { name: String, base: usize, size: usize, interrupt: Option, registers: Vec, } #[derive(Default, Debug)] struct MemoryRegion { name: String, base: usize, size: usize, } #[derive(Default, Debug)] struct Description { peripherals: Vec, memory_regions: Vec, } pub fn get_base(value: &str) -> (&str, u32) { if value.starts_with("0x") { (value.trim_start_matches("0x"), 16) } else if value.starts_with("0X") { (value.trim_start_matches("0X"), 16) } else if value.starts_with("0b") { (value.trim_start_matches("0b"), 2) } else if value.starts_with("0B") { (value.trim_start_matches("0B"), 2) } else if value.starts_with('0') && value != "0" { (value.trim_start_matches('0'), 8) } else { (value, 10) } } fn parse_usize(value: &[u8]) -> Result { let value_as_str = String::from_utf8(value.to_vec()).or(Err(ParseError::NonUTF8))?; let (value, base) = get_base(&value_as_str); usize::from_str_radix(value, base).or(Err(ParseError::ParseIntError)) } fn extract_contents(reader: &mut Reader) -> Result { let mut buf = Vec::new(); let contents = reader .read_event(&mut buf) .map_err(|_| ParseError::UnexpectedTag)?; match contents { Event::Text(t) => t .unescape_and_decode(reader) .map_err(|_| ParseError::NonUTF8), _ => Err(ParseError::UnexpectedTag), } } fn generate_field(reader: &mut Reader) -> Result { let mut buf = Vec::new(); let mut name = None; let mut lsb = None; let mut msb = None; loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => { let tag_name = e .unescape_and_decode(reader) .map_err(|_| ParseError::NonUTF8)?; match tag_name.as_str() { "name" => name = Some(extract_contents(reader)?), "lsb" => lsb = Some(parse_usize(extract_contents(reader)?.as_bytes())?), "msb" => msb = Some(parse_usize(extract_contents(reader)?.as_bytes())?), _ => (), } } Ok(Event::End(ref e)) => { if let b"field" = e.name() { break; } } Ok(_) => (), Err(e) => panic!("error parsing: {:?}", e), } } Ok(Field { name: name.ok_or(ParseError::MissingValue)?, lsb: lsb.ok_or(ParseError::MissingValue)?, msb: msb.ok_or(ParseError::MissingValue)?, }) } fn generate_fields( reader: &mut Reader, fields: &mut Vec, ) -> Result<(), ParseError> { let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => match e.name() { b"field" => fields.push(generate_field(reader)?), _ => panic!("unexpected tag in : {:?}", e), }, Ok(Event::End(ref e)) => match e.name() { b"fields" => { // println!("End fields"); break; } e => panic!("unhandled value: {:?}", e), }, Ok(Event::Text(_)) => (), e => panic!("unhandled value: {:?}", e), } } Ok(()) } fn generate_register(reader: &mut Reader) -> Result { let mut buf = Vec::new(); let mut name = None; let mut offset = None; let description = None; let mut fields = vec![]; loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => { let tag_name = e .unescape_and_decode(reader) .map_err(|_| ParseError::NonUTF8)?; match tag_name.as_str() { "name" => name = Some(extract_contents(reader)?), "addressOffset" => { offset = Some(parse_usize(extract_contents(reader)?.as_bytes())?) } "fields" => generate_fields(reader, &mut fields)?, _ => (), } } Ok(Event::End(ref e)) => { if let b"register" = e.name() { break; } } Ok(_) => (), Err(e) => panic!("error parsing: {:?}", e), } } Ok(Register { name: name.ok_or(ParseError::MissingValue)?, offset: offset.ok_or(ParseError::MissingValue)?, description, fields, }) } fn generate_registers( reader: &mut Reader, registers: &mut Vec, ) -> Result<(), ParseError> { let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => match e.name() { b"register" => registers.push(generate_register(reader)?), _ => panic!("unexpected tag in : {:?}", e), }, Ok(Event::End(ref e)) => match e.name() { b"registers" => { break; } e => panic!("unhandled value: {:?}", e), }, Ok(Event::Text(_)) => (), e => panic!("unhandled value: {:?}", e), } } Ok(()) } fn generate_peripheral(reader: &mut Reader) -> Result { let mut buf = Vec::new(); let mut name = None; let mut base = None; let mut size = None; let mut registers = vec![]; loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => { let tag_name = e .unescape_and_decode(reader) .map_err(|_| ParseError::NonUTF8)?; match tag_name.as_str() { "name" => name = Some(extract_contents(reader)?), "baseAddress" => { base = Some(parse_usize(extract_contents(reader)?.as_bytes())?) } "size" => size = Some(parse_usize(extract_contents(reader)?.as_bytes())?), "registers" => generate_registers(reader, &mut registers)?, _ => (), } } Ok(Event::End(ref e)) => { if let b"peripheral" = e.name() { break; } } Ok(_) => (), Err(e) => panic!("error parsing: {:?}", e), } } Ok(Peripheral { name: name.ok_or(ParseError::MissingValue)?, base: base.ok_or(ParseError::MissingValue)?, size: size.ok_or(ParseError::MissingValue)?, interrupt: None, registers, }) } fn generate_peripherals(reader: &mut Reader) -> Result, ParseError> { let mut buf = Vec::new(); let mut peripherals = vec![]; loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => match e.name() { b"peripheral" => peripherals.push(generate_peripheral(reader)?), _ => panic!("unexpected tag in : {:?}", e), }, Ok(Event::End(ref e)) => match e.name() { b"peripherals" => { break; } e => panic!("unhandled value: {:?}", e), }, Ok(Event::Text(_)) => (), e => panic!("unhandled value: {:?}", e), } } Ok(peripherals) } fn generate_memory_region(reader: &mut Reader) -> Result { let mut buf = Vec::new(); let mut name = None; let mut base = None; let mut size = None; loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => { let tag_name = e .unescape_and_decode(reader) .map_err(|_| ParseError::NonUTF8)?; match tag_name.as_str() { "name" => name = Some(extract_contents(reader)?), "baseAddress" => { base = Some(parse_usize(extract_contents(reader)?.as_bytes())?) } "size" => size = Some(parse_usize(extract_contents(reader)?.as_bytes())?), _ => (), } } Ok(Event::End(ref e)) => { if let b"memoryRegion" = e.name() { break; } } Ok(_) => (), Err(e) => panic!("error parsing: {:?}", e), } } Ok(MemoryRegion { name: name.ok_or(ParseError::MissingValue)?, base: base.ok_or(ParseError::MissingValue)?, size: size.ok_or(ParseError::MissingValue)?, }) } fn parse_memory_regions( reader: &mut Reader, description: &mut Description, ) -> Result<(), ParseError> { let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => match e.name() { b"memoryRegion" => description .memory_regions .push(generate_memory_region(reader)?), _ => panic!("unexpected tag in : {:?}", e), }, Ok(Event::End(ref e)) => match e.name() { b"memoryRegions" => { break; } e => panic!("unhandled value: {:?}", e), }, Ok(Event::Text(_)) => (), e => panic!("unhandled value: {:?}", e), } } Ok(()) } fn parse_vendor_extensions( reader: &mut Reader, description: &mut Description, ) -> Result<(), ParseError> { let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => match e.name() { b"memoryRegions" => parse_memory_regions(reader, description)?, _ => panic!("unexpected tag in : {:?}", e), }, Ok(Event::End(ref e)) => match e.name() { b"vendorExtensions" => { break; } e => panic!("unhandled value: {:?}", e), }, Ok(Event::Text(_)) => (), e => panic!("unhandled value: {:?}", e), } } Ok(()) } fn print_header(out: &mut U) -> std::io::Result<()> { let s = r####" #![cfg_attr(target_os = "none", no_std)] use core::convert::TryInto; pub struct Register { /// Offset of this register within this CSR offset: usize, } impl Register { pub const fn new(offset: usize) -> Register { Register { offset } } } pub struct Field { /// A bitmask we use to AND to the value, unshifted. /// E.g. for a width of `3` bits, this mask would be 0b111. mask: usize, /// Offset of the first bit in this field offset: usize, /// A copy of the register address that this field /// is a member of. Ideally this is optimized out by the /// compiler. register: Register, } impl Field { /// Define a new CSR field with the given width at a specified /// offset from the start of the register. pub const fn new(width: usize, offset: usize, register: Register) -> Field { // Asserts don't work in const fn yet. // assert!(width != 0, "field width cannot be 0"); // assert!((width + offset) < 32, "field with and offset must fit within a 32-bit value"); // It would be lovely if we could call `usize::pow()` in a const fn. let mask = match width { 0 => 0, 1 => 1, 2 => 3, 3 => 7, 4 => 15, 5 => 31, 6 => 63, 7 => 127, 8 => 255, 9 => 511, 10 => 1023, 11 => 2047, 12 => 4095, 13 => 8191, 14 => 16383, 15 => 32767, 16 => 65535, 17 => 131071, 18 => 262143, 19 => 524287, 20 => 1048575, 21 => 2097151, 22 => 4194303, 23 => 8388607, 24 => 16777215, 25 => 33554431, 26 => 67108863, 27 => 134217727, 28 => 268435455, 29 => 536870911, 30 => 1073741823, 31 => 2147483647, _ => 0, }; Field { mask, offset, register, } } } pub struct CSR { base: *mut T, } impl CSR where T: core::convert::TryFrom + core::convert::TryInto + core::default::Default, { pub fn new(base: *mut T) -> Self { CSR { base } } /// Read the contents of this register pub fn r(&mut self, reg: Register) -> T { let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) }; unsafe { usize_base.add(reg.offset).read_volatile() } .try_into() .unwrap_or_default() } /// Read a field from this CSR pub fn rf(&mut self, field: Field) -> T { let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) }; ((unsafe { usize_base.add(field.register.offset).read_volatile() } >> field.offset) & field.mask) .try_into() .unwrap_or_default() } /// Read-modify-write a given field in this CSR pub fn rmwf(&mut self, field: Field, value: T) { let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) }; let value_as_usize: usize = value.try_into().unwrap_or_default() << field.offset; let previous = unsafe { usize_base.add(field.register.offset).read_volatile() } & !field.mask; unsafe { usize_base .add(field.register.offset) .write_volatile(previous | value_as_usize) }; } /// Write a given field without reading it first pub fn wfo(&mut self, field: Field, value: T) { let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) }; let value_as_usize: usize = (value.try_into().unwrap_or_default() & field.mask) << field.offset; unsafe { usize_base .add(field.register.offset) .write_volatile(value_as_usize) }; } /// Write the entire contents of a register without reading it first pub fn wo(&mut self, reg: Register, value: T) { let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) }; let value_as_usize: usize = value.try_into().unwrap_or_default(); unsafe { usize_base.add(reg.offset).write_volatile(value_as_usize) }; } /// Zero a field from a provided value pub fn zf(&mut self, field: Field, value: T) -> T { let value_as_usize: usize = value.try_into().unwrap_or_default(); (value_as_usize & !(field.mask << field.offset)) .try_into() .unwrap_or_default() } /// Shift & mask a value to its final field position pub fn ms(&mut self, field: Field, value: T) -> T { let value_as_usize: usize = value.try_into().unwrap_or_default(); ((value_as_usize & field.mask) << field.offset) .try_into() .unwrap_or_default() } } "####; out.write_all(s.as_bytes()) } fn print_memory_regions(regions: &[MemoryRegion], out: &mut U) -> std::io::Result<()> { writeln!(out, "// Physical base addresses of memory regions")?; for region in regions { writeln!( out, "pub const HW_{}_MEM: u32 = 0x{:08x};", region.name, region.base )?; writeln!( out, "pub const HW_{}_MEM_LEN: u32 = {};", region.name, region.size )?; } writeln!(out)?; Ok(()) } fn print_peripherals(peripherals: &[Peripheral], out: &mut U) -> std::io::Result<()> { writeln!(out, "// Physical base addresses of registers")?; for peripheral in peripherals { writeln!( out, "pub const HW_{}_BASE : u32 = {};", peripheral.name, peripheral.base )?; } writeln!(out)?; writeln!(out, "pub mod utra {{")?; for peripheral in peripherals { writeln!(out)?; writeln!(out, " pub mod {} {{", peripheral.name.to_lowercase())?; for register in &peripheral.registers { writeln!(out)?; if let Some(description) = ®ister.description { writeln!(out, " /// {}", description)?; } writeln!( out, " pub const {}: crate::Register = crate::Register::new({});", register.name, register.offset )?; for field in ®ister.fields { writeln!( out, " pub const {}_{}: crate::Field = crate::Field::new({}, {}, {});", register.name, field.name.to_uppercase(), field.msb + 1 - field.lsb, field.lsb, register.name )?; } } writeln!(out, " }}")?; } writeln!(out, "}}")?; Ok(()) } fn print_tests(peripherals: &[Peripheral], out: &mut U) -> std::io::Result<()> { let test_header = r####" #[cfg(test)] mod tests { #[test] #[ignore] fn compile_check() { use super::*; "####.as_bytes(); out.write_all(test_header)?; for peripheral in peripherals { // name = peripheral.find('name') // peri_base = 'HW_' + name.text + '_BASE' // registers = peripheral.find('registers') let mod_name = peripheral.name.to_lowercase(); let reg_name = peripheral.name.to_uppercase() + "_csr"; writeln!(out, " let mut {}_csr = CSR::new(HW_{}_BASE as *mut u32);", peripheral.name.to_lowercase(), peripheral.name.to_uppercase())?; for register in &peripheral.registers { // name = register.find('name') // offset = register.find('addressOffset') // register_name = name.text writeln!(out, " let foo = {}.r(utra::{}::{});", reg_name, mod_name, register.name.to_lowercase())?; // lib.write(' {}.wo(utra::{}::{}, foo);\n'.format(reg_name, mod_name, register_name)) // for fields in register.find('fields'): // field = fields.find('name') // field_name = register_name + '_' + field.text.upper() // lib.write(' let bar = {}.rf(utra::{}::{});\n'.format(reg_name, mod_name, field_name)) // lib.write(' {}.rmwf(utra::{}::{}, bar);\n'.format(reg_name, mod_name, field_name)) // lib.write(' let mut baz = {}.zf(utra::{}::{}, bar);\n'.format(reg_name, mod_name, field_name)) // lib.write(' baz |= {}.ms(utra::{}::{}, 1);\n'.format(reg_name, mod_name, field_name)) // lib.write(' {}.wfo(utra::{}::{}, baz);\n'.format(reg_name, mod_name, field_name)) } // lib.write('\n') } writeln!(out, " }}")?; writeln!(out, "}}")?; Ok(()) } pub fn generate(src: T, dest: &mut U) -> Result<(), ParseError> { let mut buf = Vec::new(); let buf_reader = BufReader::new(src); let mut reader = Reader::from_reader(buf_reader); let mut description = Description::default(); loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => match e.name() { b"peripherals" => { description.peripherals = generate_peripherals(&mut reader)?; } b"vendorExtensions" => { parse_vendor_extensions(&mut reader, &mut description)?; } _ => (), }, Ok(Event::Eof) => break, Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), _ => (), } buf.clear(); } print_header(dest).unwrap(); print_memory_regions(&description.memory_regions, dest).unwrap(); print_peripherals(&description.peripherals, dest).unwrap(); print_tests(&description.peripherals, dest).unwrap(); Ok(()) }