mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
1013 lines
37 KiB
Rust
1013 lines
37 KiB
Rust
// SPDX-FileCopyrightText: 2020 Sean Cross <sean@xobs.io>
|
|
// SPDX-FileCopyrightText: 2020 bunnie <bunnie@kosagi.com>
|
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
|
|
use std::io::{BufRead, BufReader, Read, Write};
|
|
|
|
use quick_xml::events::{attributes::Attribute, Event};
|
|
use quick_xml::name::QName;
|
|
use quick_xml::reader::Reader;
|
|
|
|
#[derive(Debug)]
|
|
pub enum ParseError {
|
|
UnexpectedTag,
|
|
MissingValue,
|
|
ParseIntError,
|
|
NonUTF8,
|
|
WriteError,
|
|
UnexpectedValue,
|
|
MissingBasePeripheral(String),
|
|
}
|
|
|
|
#[derive(Default, Debug, Clone)]
|
|
pub struct Field {
|
|
name: String,
|
|
lsb: u32,
|
|
msb: u32,
|
|
}
|
|
|
|
#[derive(Default, Debug, Clone)]
|
|
pub struct Register {
|
|
name: String,
|
|
offset: u64,
|
|
description: Option<String>,
|
|
fields: Vec<Field>,
|
|
}
|
|
|
|
#[derive(Default, Debug, Clone)]
|
|
pub struct Interrupt {
|
|
name: String,
|
|
value: u64,
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct Peripheral {
|
|
name: String,
|
|
pub base: u64,
|
|
_size: u64,
|
|
interrupt: Vec<Interrupt>,
|
|
registers: Vec<Register>,
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct MemoryRegion {
|
|
pub name: String,
|
|
pub base: u64,
|
|
pub size: u64,
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct Constant {
|
|
pub name: String,
|
|
pub value: String,
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct Description {
|
|
pub peripherals: Vec<Peripheral>,
|
|
pub memory_regions: Vec<MemoryRegion>,
|
|
pub constants: Vec<Constant>,
|
|
}
|
|
|
|
impl core::fmt::Display for ParseError {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
|
use ParseError::*;
|
|
match *self {
|
|
UnexpectedTag => write!(f, "unexpected XML tag encountered"),
|
|
UnexpectedValue => write!(f, "unexpected XML tag value encountered"),
|
|
MissingValue => write!(f, "XML tag should have contained a value"),
|
|
ParseIntError => write!(f, "unable to parse number"),
|
|
NonUTF8 => write!(f, "file is not UTF-8"),
|
|
WriteError => write!(f, "unable to write destination file"),
|
|
MissingBasePeripheral(ref name) => write!(f, "undeclared base peripheral: {}", name),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for ParseError {}
|
|
|
|
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_u64(value: &[u8]) -> Result<u64, ParseError> {
|
|
let value_as_str = String::from_utf8(value.to_vec()).or(Err(ParseError::NonUTF8))?;
|
|
let (value, base) = get_base(&value_as_str);
|
|
u64::from_str_radix(value, base).or(Err(ParseError::ParseIntError))
|
|
}
|
|
|
|
fn parse_u32(value: &[u8]) -> Result<u32, ParseError> {
|
|
let value_as_str = String::from_utf8(value.to_vec()).or(Err(ParseError::NonUTF8))?;
|
|
let (value, base) = get_base(&value_as_str);
|
|
u32::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_into(&mut buf).map_err(|_| ParseError::UnexpectedTag)?;
|
|
match contents {
|
|
Event::Text(t) => t.unescape().map(|s| s.to_string()).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;
|
|
let mut bit_offset = None;
|
|
let mut bit_width = None;
|
|
|
|
loop {
|
|
match reader.read_event_into(&mut buf) {
|
|
Ok(Event::Start(ref e)) => {
|
|
let tag_binding = e.name().as_ref().to_vec();
|
|
let tag_name = std::str::from_utf8(&tag_binding).unwrap();
|
|
match tag_name {
|
|
"name" if name.is_none() => name = Some(extract_contents(reader)?),
|
|
"lsb" => lsb = Some(parse_u32(extract_contents(reader)?.as_bytes())?),
|
|
"msb" => msb = Some(parse_u32(extract_contents(reader)?.as_bytes())?),
|
|
"bitRange" => {
|
|
let range = extract_contents(reader)?;
|
|
if !range.starts_with('[') || !range.ends_with(']') {
|
|
return Err(ParseError::UnexpectedValue);
|
|
}
|
|
|
|
let mut parts = range[1..range.len() - 1].split(':');
|
|
msb = Some(
|
|
parts
|
|
.next()
|
|
.ok_or(ParseError::UnexpectedValue)?
|
|
.parse::<u32>()
|
|
.map_err(|_| ParseError::ParseIntError)?,
|
|
);
|
|
lsb = Some(
|
|
parts
|
|
.next()
|
|
.ok_or(ParseError::UnexpectedValue)?
|
|
.parse::<u32>()
|
|
.map_err(|_| ParseError::ParseIntError)?,
|
|
);
|
|
}
|
|
"bitWidth" => bit_width = Some(parse_u32(extract_contents(reader)?.as_bytes())?),
|
|
"bitOffset" => bit_offset = Some(parse_u32(extract_contents(reader)?.as_bytes())?),
|
|
_ => (),
|
|
}
|
|
}
|
|
Ok(Event::End(ref e)) => {
|
|
if let b"field" = e.local_name().as_ref() {
|
|
break;
|
|
}
|
|
}
|
|
Ok(_) => (),
|
|
Err(e) => panic!("error parsing: {:?}", e),
|
|
}
|
|
}
|
|
|
|
// If no msb/lsb and bitRange tags were encountered then
|
|
// it's possible that the field is defined via
|
|
// `bitWidth` and `bitOffset` tags instead. Let's handle this.
|
|
if lsb.is_none() && msb.is_none() {
|
|
if let (Some(bit_width), Some(bit_offset)) = (bit_width, bit_offset) {
|
|
lsb = Some(bit_offset);
|
|
msb = Some(bit_offset + bit_width - 1);
|
|
}
|
|
}
|
|
|
|
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_into(&mut buf) {
|
|
Ok(Event::Start(ref e)) => match e.local_name().as_ref() {
|
|
b"field" => fields.push(generate_field(reader)?),
|
|
_ => panic!("unexpected tag in <field>: {:?}", e),
|
|
},
|
|
Ok(Event::End(ref e)) => match e.local_name().as_ref() {
|
|
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_into(&mut buf) {
|
|
Ok(Event::Start(ref e)) => {
|
|
let tag_binding = e.local_name().as_ref().to_vec();
|
|
let tag_name = std::str::from_utf8(&tag_binding).map_err(|_| ParseError::NonUTF8)?;
|
|
match tag_name {
|
|
"name" => name = Some(extract_contents(reader)?),
|
|
"addressOffset" => offset = Some(parse_u64(extract_contents(reader)?.as_bytes())?),
|
|
"fields" => generate_fields(reader, &mut fields)?,
|
|
_ => (),
|
|
}
|
|
}
|
|
Ok(Event::End(ref e)) => {
|
|
if let b"register" = e.local_name().as_ref() {
|
|
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_interrupts<T: BufRead>(
|
|
reader: &mut Reader<T>,
|
|
interrupts: &mut Vec<Interrupt>,
|
|
) -> Result<(), ParseError> {
|
|
let mut buf = Vec::new();
|
|
let mut name = None;
|
|
let mut value = None;
|
|
loop {
|
|
match reader.read_event_into(&mut buf) {
|
|
Ok(Event::Start(ref e)) => {
|
|
let tag_binding = e.local_name().as_ref().to_vec();
|
|
let tag_name = std::str::from_utf8(&tag_binding).map_err(|_| ParseError::NonUTF8)?;
|
|
match tag_name {
|
|
"name" => name = Some(extract_contents(reader)?),
|
|
"value" => value = Some(parse_u64(extract_contents(reader)?.as_bytes())?),
|
|
_ => (),
|
|
}
|
|
}
|
|
Ok(Event::End(ref e)) => {
|
|
if let b"interrupt" = e.local_name().as_ref() {
|
|
break;
|
|
}
|
|
}
|
|
Ok(_) => (),
|
|
Err(e) => panic!("error parsing: {:?}", e),
|
|
}
|
|
}
|
|
|
|
interrupts.push(Interrupt {
|
|
name: name.ok_or(ParseError::MissingValue)?,
|
|
value: value.ok_or(ParseError::MissingValue)?,
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
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_into(&mut buf) {
|
|
Ok(Event::Start(ref e)) => match e.local_name().as_ref() {
|
|
b"register" => registers.push(generate_register(reader)?),
|
|
_ => panic!("unexpected tag in <registers>: {:?}", e),
|
|
},
|
|
Ok(Event::End(ref e)) => match e.local_name().as_ref() {
|
|
b"registers" => {
|
|
break;
|
|
}
|
|
e => panic!("unhandled value: {:?}", e),
|
|
},
|
|
Ok(Event::Text(_)) => (),
|
|
e => panic!("unhandled value: {:?}", e),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn derive_peripheral(base: &Peripheral, child_name: &str, child_base: u64) -> Peripheral {
|
|
Peripheral {
|
|
name: child_name.to_owned(),
|
|
base: child_base,
|
|
_size: base._size,
|
|
interrupt: base.interrupt.clone(),
|
|
registers: base.registers.clone(),
|
|
}
|
|
}
|
|
|
|
fn generate_peripheral<T: BufRead>(
|
|
base_peripheral: Option<&Peripheral>,
|
|
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![];
|
|
let mut interrupts = vec![];
|
|
|
|
loop {
|
|
match reader.read_event_into(&mut buf) {
|
|
Ok(Event::Start(ref e)) => {
|
|
let tag_binding = e.local_name().as_ref().to_vec();
|
|
let tag_name = std::str::from_utf8(&tag_binding).map_err(|_| ParseError::NonUTF8)?;
|
|
match tag_name {
|
|
"name" => name = Some(extract_contents(reader)?),
|
|
"baseAddress" => base = Some(parse_u64(extract_contents(reader)?.as_bytes())?),
|
|
"size" => size = Some(parse_u64(extract_contents(reader)?.as_bytes())?),
|
|
"registers" => generate_registers(reader, &mut registers)?,
|
|
"interrupt" => generate_interrupts(reader, &mut interrupts)?,
|
|
_ => (),
|
|
}
|
|
}
|
|
Ok(Event::End(ref e)) => {
|
|
if let b"peripheral" = e.local_name().as_ref() {
|
|
break;
|
|
}
|
|
}
|
|
Ok(_) => (),
|
|
Err(e) => panic!("error parsing: {:?}", e),
|
|
}
|
|
}
|
|
|
|
let name = name.ok_or(ParseError::MissingValue)?;
|
|
let base = base.ok_or(ParseError::MissingValue)?;
|
|
|
|
// Derive from the base peripheral if specified
|
|
if let Some(base_peripheral) = base_peripheral {
|
|
Ok(derive_peripheral(base_peripheral, &name, base))
|
|
} else {
|
|
Ok(Peripheral {
|
|
name,
|
|
base,
|
|
_size: size.ok_or(ParseError::MissingValue)?,
|
|
interrupt: interrupts,
|
|
registers,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn generate_peripherals<T: BufRead>(reader: &mut Reader<T>) -> Result<Vec<Peripheral>, ParseError> {
|
|
let mut buf = Vec::new();
|
|
let mut peripherals: Vec<Peripheral> = vec![];
|
|
|
|
loop {
|
|
match reader.read_event_into(&mut buf) {
|
|
Ok(Event::Start(ref e)) => match e.local_name().as_ref() {
|
|
b"peripheral" => {
|
|
let base_peripheral = match e.attributes().next() {
|
|
Some(Ok(Attribute { key, value })) if key == QName(b"derivedFrom") => {
|
|
let base_peripheral_name =
|
|
String::from_utf8(value.to_vec()).map_err(|_| ParseError::NonUTF8)?;
|
|
|
|
let base = peripherals
|
|
.iter()
|
|
.find(|p| p.name == base_peripheral_name)
|
|
.ok_or(ParseError::MissingBasePeripheral(base_peripheral_name))?;
|
|
|
|
Some(base)
|
|
}
|
|
_ => None,
|
|
};
|
|
|
|
peripherals.push(generate_peripheral(base_peripheral, reader)?);
|
|
}
|
|
_ => panic!("unexpected tag in <peripherals>: {:?}", e),
|
|
},
|
|
Ok(Event::End(ref e)) => match e.local_name().as_ref() {
|
|
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_into(&mut buf) {
|
|
Ok(Event::Start(ref e)) => {
|
|
let tag_binding = e.local_name().as_ref().to_vec();
|
|
let tag_name = std::str::from_utf8(&tag_binding).map_err(|_| ParseError::NonUTF8)?;
|
|
match tag_name {
|
|
"name" => name = Some(extract_contents(reader)?),
|
|
"baseAddress" => base = Some(parse_u64(extract_contents(reader)?.as_bytes())?),
|
|
"size" => size = Some(parse_u64(extract_contents(reader)?.as_bytes())?),
|
|
_ => (),
|
|
}
|
|
}
|
|
Ok(Event::End(ref e)) => {
|
|
if let b"memoryRegion" = e.local_name().as_ref() {
|
|
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_into(&mut buf) {
|
|
Ok(Event::Start(ref e)) => match e.name() {
|
|
QName(b"memoryRegion") => {
|
|
let mut mr = generate_memory_region(reader)?;
|
|
// keep adding _ to the end of the name until it's unique
|
|
loop {
|
|
if description.memory_regions.iter().find(|&m| m.name == mr.name).is_some() {
|
|
mr.name.push_str("_X");
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
description.memory_regions.push(mr)
|
|
}
|
|
_ => panic!("unexpected tag in <memoryRegions>: {:?}", e),
|
|
},
|
|
Ok(Event::End(ref e)) => match e.name() {
|
|
QName(b"memoryRegions") => {
|
|
break;
|
|
}
|
|
e => panic!("unhandled value: {:?}", e),
|
|
},
|
|
Ok(Event::Text(_)) => (),
|
|
e => panic!("unhandled value: {:?}", e),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn generate_constants<T: BufRead>(
|
|
reader: &mut Reader<T>,
|
|
description: &mut Description,
|
|
) -> Result<(), ParseError> {
|
|
let mut buf = Vec::new();
|
|
loop {
|
|
match reader.read_event_into(&mut buf) {
|
|
Ok(Event::Empty(ref e)) => match e.name() {
|
|
QName(b"constant") => {
|
|
let mut constant_descriptor = Constant::default();
|
|
for maybe_att in e.attributes() {
|
|
match maybe_att {
|
|
Ok(att) => {
|
|
let att_name = String::from_utf8(att.key.local_name().as_ref().into())
|
|
.expect("constant: error parsing attribute name");
|
|
let att_value = String::from_utf8(att.value.to_vec())
|
|
.expect("constant: error parsing attribute value");
|
|
match att_name {
|
|
_ if att_name == "name" => constant_descriptor.name = att_value,
|
|
_ if att_name == "value" => constant_descriptor.value = att_value,
|
|
_ => panic!("unexpected attribute name"),
|
|
}
|
|
}
|
|
_ => panic!("unexpected value in constant: {:?}", maybe_att),
|
|
}
|
|
}
|
|
// keep adding _ to the end of the name until it's unique
|
|
loop {
|
|
if description
|
|
.constants
|
|
.iter()
|
|
.find(|&c| c.name == constant_descriptor.name)
|
|
.is_some()
|
|
{
|
|
constant_descriptor.name.push_str("_X");
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
description.constants.push(constant_descriptor)
|
|
}
|
|
_ => panic!("unexpected tag in <constants>: {:?}", e),
|
|
},
|
|
// note to future self: if Litex goe away from attributes to nested elements, you would want
|
|
// Ok(Event::Start(ref e) => match e.name() ... to descend into the next tag level, and then
|
|
// use the tag_name match and extract_contents methods from other functions to generate
|
|
// the structure.
|
|
// note that the two formats could be mutually exclusively compatible within the same code base:
|
|
// if there are no attributes, the attribute iterator would do nothing; and if there are no
|
|
// child elements, the recursive descent would also do nothing.
|
|
Ok(Event::End(ref e)) => match e.name() {
|
|
QName(b"constants") => 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_into(&mut buf) {
|
|
Ok(Event::Start(ref e)) => match e.name() {
|
|
QName(b"memoryRegions") => parse_memory_regions(reader, description)?,
|
|
QName(b"constants") => generate_constants(reader, description)?,
|
|
_ => panic!("unexpected tag in <vendorExtensions>: {:?}", e),
|
|
},
|
|
Ok(Event::End(ref e)) => match e.name() {
|
|
QName(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(rustfmt, rustfmt_skip)] // don't format generated files
|
|
#![allow(dead_code)]
|
|
use core::convert::TryInto;
|
|
#[cfg(feature="std")]
|
|
use core::sync::atomic::AtomicPtr;
|
|
#[cfg(feature="std")]
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct Register {
|
|
/// Offset of this register within this CSR
|
|
offset: usize,
|
|
/// Mask of SVD-specified bits for the register
|
|
mask: usize,
|
|
}
|
|
impl Register {
|
|
pub const fn new(offset: usize, mask: usize) -> Register {
|
|
Register { offset, mask }
|
|
}
|
|
pub const fn offset(&self) -> usize { self.offset }
|
|
pub const fn mask(&self) -> usize { self.mask }
|
|
}
|
|
#[derive(Debug, Copy, Clone)]
|
|
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 {
|
|
let mask = if width < 32 { (1 << width) - 1 } else {0xFFFF_FFFF};
|
|
Field {
|
|
mask,
|
|
offset,
|
|
register,
|
|
}
|
|
}
|
|
pub const fn offset(&self) -> usize { self.offset }
|
|
pub const fn mask(&self) -> usize { self.mask }
|
|
}
|
|
#[derive(Debug, Copy, Clone)]
|
|
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 }
|
|
}
|
|
/// Retrieve the raw pointer used as the base of the CSR. This is unsafe because the copied
|
|
/// value can be used to do all kinds of awful shared mutable operations (like creating
|
|
/// another CSR accessor owned by another thread). However, sometimes this is unavoidable
|
|
/// because hardware is in fact shared mutable state.
|
|
pub unsafe fn base(&self) -> *mut T {
|
|
self.base
|
|
}
|
|
/// Read the contents of this register
|
|
pub fn r(&self, reg: Register) -> T {
|
|
// prevent re-ordering
|
|
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
|
|
|
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(&self, field: Field) -> T {
|
|
// prevent re-ordering
|
|
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
|
|
|
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 << field.offset);
|
|
unsafe {
|
|
usize_base
|
|
.add(field.register.offset)
|
|
.write_volatile(previous | value_as_usize)
|
|
};
|
|
// prevent re-ordering
|
|
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
|
}
|
|
/// 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)
|
|
};
|
|
// Ensure the compiler doesn't re-order the write.
|
|
// We use `SeqCst`, because `Acquire` only prevents later accesses from being reordered before
|
|
// *reads*, but this method only *writes* to the locations.
|
|
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
|
}
|
|
/// 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) };
|
|
// Ensure the compiler doesn't re-order the write.
|
|
// We use `SeqCst`, because `Acquire` only prevents later accesses from being reordered before
|
|
// *reads*, but this method only *writes* to the locations.
|
|
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
|
}
|
|
/// Zero a field from a provided value
|
|
pub fn zf(&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(&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()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
#[cfg(feature="std")]
|
|
pub struct AtomicCsr<T> {
|
|
base: Arc::<AtomicPtr<T>>,
|
|
}
|
|
#[cfg(feature="std")]
|
|
impl<T> AtomicCsr<T>
|
|
where
|
|
T: core::convert::TryFrom<usize> + core::convert::TryInto<usize> + core::default::Default,
|
|
{
|
|
/// AtomicCsr wraps the CSR in an Arc + AtomicPtr, so that write operations don't require
|
|
/// a mutable reference. This allows us to stick CSR accesses into APIs that require
|
|
/// non-mutable references to hardware state (such as certain "standardized" USB APIs).
|
|
/// Hiding the fact that you're tweaking hardware registers behind Arc/AtomicPtr seems a little
|
|
/// scary, but, it does make for nicer Rust semantics.
|
|
pub fn new(base: *mut T) -> Self {
|
|
AtomicCsr {
|
|
base: Arc::new(AtomicPtr::new(base))
|
|
}
|
|
}
|
|
pub fn clone(&self) -> Self {
|
|
AtomicCsr {
|
|
base: self.base.clone()
|
|
}
|
|
}
|
|
/// Read the contents of this register
|
|
pub fn r(&self, reg: Register) -> T {
|
|
// prevent re-ordering
|
|
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
|
|
|
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base.load(core::sync::atomic::Ordering::SeqCst)) };
|
|
unsafe { usize_base.add(reg.offset).read_volatile() }
|
|
.try_into()
|
|
.unwrap_or_default()
|
|
}
|
|
/// Read a field from this CSR
|
|
pub fn rf(&self, field: Field) -> T {
|
|
// prevent re-ordering
|
|
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
|
|
|
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base.load(core::sync::atomic::Ordering::SeqCst)) };
|
|
((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(&self, field: Field, value: T) {
|
|
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base.load(core::sync::atomic::Ordering::SeqCst)) };
|
|
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 << field.offset);
|
|
unsafe {
|
|
usize_base
|
|
.add(field.register.offset)
|
|
.write_volatile(previous | value_as_usize)
|
|
};
|
|
// prevent re-ordering
|
|
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
|
}
|
|
/// Write a given field without reading it first
|
|
pub fn wfo(&self, field: Field, value: T) {
|
|
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base.load(core::sync::atomic::Ordering::SeqCst)) };
|
|
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)
|
|
};
|
|
// Ensure the compiler doesn't re-order the write.
|
|
// We use `SeqCst`, because `Acquire` only prevents later accesses from being reordered before
|
|
// *reads*, but this method only *writes* to the locations.
|
|
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
|
}
|
|
/// Write the entire contents of a register without reading it first
|
|
pub fn wo(&self, reg: Register, value: T) {
|
|
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base.load(core::sync::atomic::Ordering::SeqCst)) };
|
|
let value_as_usize: usize = value.try_into().unwrap_or_default();
|
|
unsafe { usize_base.add(reg.offset).write_volatile(value_as_usize) };
|
|
// Ensure the compiler doesn't re-order the write.
|
|
// We use `SeqCst`, because `Acquire` only prevents later accesses from being reordered before
|
|
// *reads*, but this method only *writes* to the locations.
|
|
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
|
}
|
|
/// Zero a field from a provided value
|
|
pub fn zf(&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(&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: usize = 0x{:08x};", region.name, region.base)?;
|
|
writeln!(out, "pub const HW_{}_MEM_LEN: usize = {};", region.name, region.size)?;
|
|
}
|
|
writeln!(out)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn print_constants<U: Write>(constants: &[Constant], out: &mut U) -> std::io::Result<()> {
|
|
writeln!(out, "\n// Litex auto-generated constants")?;
|
|
for constant in constants {
|
|
let maybe_intval = constant.value.parse::<u32>();
|
|
match maybe_intval {
|
|
Ok(intval) => {
|
|
writeln!(out, "pub const LITEX_{}: usize = {};", constant.name, intval)?;
|
|
}
|
|
Err(_) => {
|
|
writeln!(out, "pub const LITEX_{}: &str = \"{}\";", constant.name, constant.value)?;
|
|
}
|
|
}
|
|
}
|
|
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 : usize = 0x{:08x};",
|
|
peripheral.name.to_uppercase(),
|
|
peripheral.base
|
|
)?;
|
|
}
|
|
writeln!(out)?;
|
|
|
|
let s = r####"
|
|
pub mod utra {
|
|
"####;
|
|
out.write_all(s.as_bytes())?;
|
|
|
|
for peripheral in peripherals {
|
|
writeln!(out)?;
|
|
writeln!(out, " pub mod {} {{", peripheral.name.to_lowercase())?;
|
|
writeln!(
|
|
out,
|
|
" pub const {}_NUMREGS: usize = {};",
|
|
peripheral.name.to_uppercase(),
|
|
peripheral.registers.len()
|
|
)?;
|
|
for register in &peripheral.registers {
|
|
writeln!(out)?;
|
|
if let Some(description) = ®ister.description {
|
|
writeln!(out, " /// {}", description)?;
|
|
}
|
|
let mut mask: usize = 0;
|
|
for field in ®ister.fields {
|
|
mask |= ((1 << (field.msb + 1 - field.lsb)) - 1) << field.lsb;
|
|
}
|
|
writeln!(
|
|
out,
|
|
" pub const {}: crate::Register = crate::Register::new({}, 0x{:x});",
|
|
register.name.to_uppercase(),
|
|
register.offset / 4,
|
|
mask,
|
|
)?;
|
|
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)?;
|
|
for interrupt in &peripheral.interrupt {
|
|
writeln!(
|
|
out,
|
|
" pub const {}_IRQ: usize = {};",
|
|
interrupt.name.to_uppercase(),
|
|
interrupt.value
|
|
)?;
|
|
}
|
|
writeln!(
|
|
out,
|
|
" pub const HW_{}_BASE: usize = 0x{:08x};",
|
|
peripheral.name.to_uppercase(),
|
|
peripheral.base
|
|
)?;
|
|
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 {
|
|
"####
|
|
.as_bytes();
|
|
out.write_all(test_header)?;
|
|
|
|
for peripheral in peripherals {
|
|
let mod_name = peripheral.name.to_lowercase();
|
|
let per_name = peripheral.name.to_lowercase() + "_csr";
|
|
|
|
write!(
|
|
out,
|
|
r####"
|
|
#[test]
|
|
#[ignore]
|
|
fn compile_check_{}() {{
|
|
use super::*;
|
|
"####,
|
|
per_name
|
|
)?;
|
|
|
|
writeln!(
|
|
out,
|
|
" let mut {} = CSR::new(HW_{}_BASE as *mut u32);",
|
|
per_name,
|
|
peripheral.name.to_uppercase()
|
|
)?;
|
|
for register in &peripheral.registers {
|
|
writeln!(out)?;
|
|
let reg_name = register.name.to_uppercase();
|
|
writeln!(out, " let foo = {}.r(utra::{}::{});", per_name, mod_name, reg_name)?;
|
|
writeln!(out, " {}.wo(utra::{}::{}, foo);", per_name, mod_name, reg_name)?;
|
|
for field in ®ister.fields {
|
|
let field_name = format!("{}_{}", reg_name, field.name.to_uppercase());
|
|
writeln!(out, " let bar = {}.rf(utra::{}::{});", per_name, mod_name, field_name)?;
|
|
writeln!(out, " {}.rmwf(utra::{}::{}, bar);", per_name, mod_name, field_name)?;
|
|
writeln!(
|
|
out,
|
|
" let mut baz = {}.zf(utra::{}::{}, bar);",
|
|
per_name, mod_name, field_name
|
|
)?;
|
|
writeln!(out, " baz |= {}.ms(utra::{}::{}, 1);", per_name, mod_name, field_name)?;
|
|
writeln!(out, " {}.wfo(utra::{}::{}, baz);", per_name, mod_name, field_name)?;
|
|
}
|
|
}
|
|
|
|
writeln!(out, " }}")?;
|
|
}
|
|
writeln!(out, "}}")?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn parse_svd<T: Read>(sources: Vec<T>) -> Result<Description, ParseError> {
|
|
let mut description = Description::default();
|
|
for src in sources {
|
|
let mut buf = Vec::new();
|
|
let buf_reader = BufReader::new(src);
|
|
let mut reader = Reader::from_reader(buf_reader);
|
|
loop {
|
|
match reader.read_event_into(&mut buf) {
|
|
Ok(Event::Start(ref e)) => match e.name() {
|
|
QName(b"peripherals") => {
|
|
description.peripherals.append(&mut generate_peripherals(&mut reader)?);
|
|
}
|
|
QName(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();
|
|
}
|
|
}
|
|
Ok(description)
|
|
}
|
|
|
|
pub fn generate<T: Read, U: Write>(src: Vec<T>, dest: &mut U) -> Result<(), ParseError> {
|
|
let description = parse_svd(src)?;
|
|
|
|
print_header(dest).or(Err(ParseError::WriteError))?;
|
|
print_memory_regions(&description.memory_regions, dest).or(Err(ParseError::WriteError))?;
|
|
print_peripherals(&description.peripherals, dest).or(Err(ParseError::WriteError))?;
|
|
print_constants(&description.constants, dest).or(Err(ParseError::WriteError))?;
|
|
print_tests(&description.peripherals, dest).or(Err(ParseError::WriteError))?;
|
|
|
|
Ok(())
|
|
}
|