svd-generator 0.1.0

Converts device information from flattened device tree into an SVD description
Documentation
use crate::tree::{parse_reg, Compatible};
use crate::{Error, Result};

pub mod dw_apb_i2c;
pub mod dw_apb_uart;
pub mod pl022_ssp_spi;

use dw_apb_i2c::DwApbI2c;
use dw_apb_uart::DwApbUart;
use pl022_ssp_spi::Pl022SspSpi;

/// Represents the number of peripherals in the SVD device description.
pub struct PeripheralCount {
    /// Number of I2C peripherals
    pub i2c: usize,
    /// Number of SPI peripherals
    pub spi: usize,
    /// Number of UART peripherals
    pub uart: usize,
}

impl PeripheralCount {
    /// Creates a new [PeripheralCount].
    pub const fn new() -> Self {
        Self {
            i2c: 0,
            spi: 0,
            uart: 0,
        }
    }
}

/// Convenience function to create an SVD [`Peripheral`](svd::Peripheral).
pub fn create_peripheral(
    name: &str,
    desc: &str,
    base_address: u64,
    size: u32,
    interrupt: Option<Vec<svd::Interrupt>>,
    registers: Option<Vec<svd::RegisterCluster>>,
    dim_element: Option<svd::DimElement>,
) -> Result<svd::Peripheral> {
    let info = svd::PeripheralInfo::builder()
        .name(name.into())
        .description(Some(desc.into()))
        .base_address(base_address)
        .address_block(Some(
            [svd::AddressBlock::builder()
                .offset(0)
                .size(size)
                .usage(svd::AddressBlockUsage::Registers)
                .build(svd::ValidateLevel::Strict)?]
            .into(),
        ))
        .interrupt(interrupt)
        .registers(registers)
        .build(svd::ValidateLevel::Weak)?;

    match dim_element {
        Some(dim) => Ok(svd::Peripheral::Array(info, dim)),
        None => Ok(svd::Peripheral::Single(info)),
    }
}

/// Parse SVD peripheral information from the DeviceTree root [`Node`](device_tree::Node).
///
/// # Examples
///
/// ```no_run
/// use device_tree::DeviceTree;
/// use svd_generator::svd::parse_peripherals;
///
/// // just an example
/// // this would be bytes loaded from a flattened DeviceTree file
/// let dt_buf = &[0u8];
/// let dt = DeviceTree::load(dt_buf).unwrap();
/// let _peripherals = parse_peripherals(&dt.root).unwrap();
/// ```
pub fn parse_peripherals(root: &device_tree::Node) -> Result<Vec<svd::Peripheral>> {
    let mut count = PeripheralCount::new();

    let addr_cells = root.prop_u32("#address-cells").unwrap_or(1) as usize;
    let size_cells = root.prop_u32("#size-cells").unwrap_or(1) as usize;

    Ok(root
        .children
        .iter()
        .find(|n| n.name == "soc")
        .ok_or(Error::Svd("no `soc` node found in DeviceTree".into()))?
        .children
        .iter()
        .filter(|n| match n.prop_str("compatible") {
            Ok(p) => {
                log::debug!("Found peripheral `compatible` property: {p}");
                Compatible::from(p).is_known()
            }
            Err(_) => false,
        })
        .filter_map(|n| parse_peripheral(n, addr_cells, size_cells, &mut count).ok())
        .collect())
}

fn parse_peripheral(
    node: &device_tree::Node,
    addr_cells: usize,
    size_cells: usize,
    count: &mut PeripheralCount,
) -> Result<svd::Peripheral> {
    let name = node.name.as_str();
    let comp_str = node.prop_str("compatible")?;
    let comp = Compatible::from(comp_str);

    let reg_names = node.prop_str("reg-names").unwrap_or("");
    let desc = format!("{name} {comp_str},{reg_names} peripheral generator");

    let (address, size) = parse_reg(node, addr_cells, size_cells)?;

    Ok(match comp {
        Compatible::DwApbI2c => {
            let i2c_name = format!("i2c{}", count.i2c);
            count.i2c += 1;

            let interrupt = if node.has_prop("interrrupts") {
                Some(vec![svd::Interrupt::builder()
                    .name(i2c_name.to_uppercase())
                    .value(node.prop_u32("interrupts")?)
                    .build(svd::ValidateLevel::Strict)?])
            } else {
                None
            };

            DwApbI2c::create(i2c_name.as_str(), address, size, interrupt, 0)?.to_inner()
        }
        Compatible::DwApbUart => {
            let uart_name = format!("uart{}", count.uart);
            count.uart += 1;

            let interrupt = if node.has_prop("interrrupts") {
                Some(vec![svd::Interrupt::builder()
                    .name(uart_name.to_uppercase())
                    .value(node.prop_u32("interrupts")?)
                    .build(svd::ValidateLevel::Strict)?])
            } else {
                None
            };

            DwApbUart::create(uart_name.as_str(), address, size, interrupt, 0)?.to_inner()
        }
        Compatible::Pl022SspSpi => {
            let spi_name = format!("spi{}", count.spi);
            count.spi += 1;

            let interrupt = if node.has_prop("interrrupts") {
                Some(vec![svd::Interrupt::builder()
                    .name(spi_name.to_uppercase())
                    .value(node.prop_u32("interrupts")?)
                    .build(svd::ValidateLevel::Strict)?])
            } else {
                None
            };

            Pl022SspSpi::create(spi_name.as_str(), address, size, interrupt, 0)?.to_inner()
        }
        _ => create_peripheral(name, desc.as_str(), address, size, None, None, None)?,
    })
}