initial commit

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2020-10-04 22:31:00 +08:00
commit 13966f9b02
6 changed files with 5409 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
Cargo.lock

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "utra"
version = "0.1.0"
authors = ["Sean Cross <sean@xobs.io>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
quick-xml = "0.19"

4742
examples/soc.svd Normal file

File diff suppressed because it is too large Load Diff

635
src/generate.rs Normal file
View File

@ -0,0 +1,635 @@
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<String>,
fields: Vec<Field>,
}
#[derive(Default, Debug)]
struct Peripheral {
name: String,
base: usize,
size: usize,
interrupt: Option<usize>,
registers: Vec<Register>,
}
#[derive(Default, Debug)]
struct MemoryRegion {
name: String,
base: usize,
size: usize,
}
#[derive(Default, Debug)]
struct Description {
peripherals: Vec<Peripheral>,
memory_regions: Vec<MemoryRegion>,
}
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<usize, ParseError> {
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<T: BufRead>(reader: &mut Reader<T>) -> Result<String, ParseError> {
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<T: BufRead>(reader: &mut Reader<T>) -> Result<Field, ParseError> {
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<T: BufRead>(
reader: &mut Reader<T>,
fields: &mut Vec<Field>,
) -> 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 <field>: {:?}", 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<T: BufRead>(reader: &mut Reader<T>) -> Result<Register, ParseError> {
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<T: BufRead>(
reader: &mut Reader<T>,
registers: &mut Vec<Register>,
) -> 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 <registers>: {:?}", 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<T: BufRead>(reader: &mut Reader<T>) -> Result<Peripheral, ParseError> {
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<T: BufRead>(reader: &mut Reader<T>) -> Result<Vec<Peripheral>, 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 <peripherals>: {:?}", 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<T: BufRead>(reader: &mut Reader<T>) -> Result<MemoryRegion, ParseError> {
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<T: BufRead>(
reader: &mut Reader<T>,
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 <memoryRegions>: {:?}", 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<T: BufRead>(
reader: &mut Reader<T>,
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 <vendorExtensions>: {:?}", 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<U: Write>(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<T> {
base: *mut T,
}
impl<T> CSR<T>
where
T: core::convert::TryFrom<usize> + core::convert::TryInto<usize> + 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<U: Write>(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<U: Write>(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) = &register.description {
writeln!(out, " /// {}", description)?;
}
writeln!(
out,
" pub const {}: crate::Register = crate::Register::new({});",
register.name, register.offset
)?;
for field in &register.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<U: Write>(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<T: Read, U: Write>(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(())
}

10
src/lib.rs Normal file
View File

@ -0,0 +1,10 @@
mod generate;
pub use generate::*;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

10
src/main.rs Normal file
View File

@ -0,0 +1,10 @@
mod generate;
use generate::*;
use std::fs::File;
fn main() {
let src = File::open("examples/soc.svd").unwrap();
let mut dest = File::create("example.rs").unwrap();
generate(src, &mut dest).unwrap();
}