psoc-drivers 0.1.0

Hardware driver implementations for psoc-rs
// Copyright (c) 2026, Infineon Technologies AG or an affiliate of Infineon Technologies AG.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing permissions and
// limitations under the License.

use psoc_devices::{DICE, Feature, FeatureValue, VALID_FEATURES, build_feature_versions};
use std::collections::{BTreeSet, HashSet};

macro_rules! error {
    ($($arg:tt)*) => {{
        println!("cargo::error={}", format!($($arg)*).replace('\n', "\\n"));
        std::process::exit(0);
    }};
}

fn main() {
    let feature_versions = build_feature_versions(VALID_FEATURES);
    for (Feature(feature), values) in &feature_versions {
        match values
            .iter()
            .next()
            .unwrap_or_else(|| panic!("{feature} has no valid values"))
        {
            FeatureValue::Flag => {
                println!("cargo:rustc-check-cfg=cfg({feature})");
            }
            FeatureValue::Version(_) => {
                println!("cargo:rustc-check-cfg=cfg({feature})");
                let versions: String = values
                    .iter()
                    .flat_map(|v| ["\"", v.contents().unwrap(), "\","])
                    .collect();
                println!("cargo:rustc-check-cfg=cfg({feature}_version, values({versions}))");
            }
            FeatureValue::Property(_) => {
                let values: String = values
                    .iter()
                    .flat_map(|v| ["\"", v.contents().unwrap(), "\","])
                    .collect();
                println!("cargo:rustc-check-cfg=cfg({feature}, values({values}))");
            }
        }
    }
    let cfg_die_values: String = DICE
        .iter()
        .flat_map(|die| ["\"", die.name, "\","])
        .collect();
    println!("cargo:rustc-check-cfg=cfg(die, values({cfg_die_values}))");

    let all_cores: BTreeSet<&str> = DICE
        .iter()
        .flat_map(|die| die.cores().iter().copied())
        .collect();
    let cfg_core_values: String = all_cores
        .iter()
        .flat_map(|core| ["\"", core, "\","])
        .collect();
    println!("cargo:rustc-check-cfg=cfg(core, values({cfg_core_values}))");

    println!("cargo:rustc-check-cfg=cfg(secure)");
    println!("cargo:rustc-check-cfg=cfg(nonsecure)");

    let mut configured_die: Option<String> = None;
    let mut configured_core: Option<String> = None;
    for (name, _) in std::env::vars() {
        if let Some(name) = name.strip_prefix("CARGO_FEATURE_DIE_") {
            if let Some(existing) = configured_die {
                error!(
                    "'die-' features are mutually exclusive (found 'die-{}' and 'die-{}')",
                    existing.to_lowercase(),
                    name.to_lowercase()
                );
            };
            configured_die = Some(name.to_lowercase());
        } else if let Some(name) = name.strip_prefix("CARGO_FEATURE_CORE_") {
            if let Some(existing) = configured_core {
                error!(
                    "'core-' features are mutually exclusive (found 'core-{}' and 'core-{}')",
                    existing.to_lowercase(),
                    name.to_lowercase()
                );
            };
            configured_core = Some(name.to_lowercase());
        }
    }

    let configured_die = configured_die.unwrap_or_else(|| error!(concat!(
        "Must configure a die",
        " (help: ensure you are depending on `psoc` with the appropriate feature selected for your part number)"
    )));

    let mut seen_die_names = HashSet::with_capacity(DICE.len());
    let mut die = None;
    for d in DICE {
        assert!(
            seen_die_names.insert(d.name.to_lowercase()),
            "Duplicate die name {}",
            d.name
        );
        if d.name.eq_ignore_ascii_case(&configured_die) {
            die = Some(d);
        }
    }
    let die = die.unwrap();

    let features = die.features(&feature_versions);
    println!("cargo:rustc-cfg=die=\"{}\"", die.name);
    for (Feature(feature), value) in &features {
        match value {
            FeatureValue::Flag => println!("cargo:rustc-cfg={feature}"),
            FeatureValue::Property(value) => {
                println!("cargo:rustc-cfg={feature}=\"{value}\"")
            }
            FeatureValue::Version(version) => {
                println!("cargo:rustc-cfg={feature}");
                println!("cargo:rustc-cfg={feature}_version=\"{version}\"");
            }
        }
    }

    let configured_core = configured_core
        .as_deref()
        .unwrap_or(die.cores().first().unwrap());
    println!("cargo:rustc-cfg=core=\"{}\"", configured_core);

    let is_secure = configured_core.ends_with("_s");
    let is_nonsecure = configured_core.ends_with("_ns");
    if features.contains_key(&Feature("trustzone")) {
        assert!(is_secure ^ is_nonsecure);
        println!(
            "cargo:rustc-cfg={}",
            if is_secure { "secure" } else { "nonsecure" }
        );
    } else {
        assert!(!is_secure && !is_nonsecure);
    }

    let expected_target = psoc_devices::target_for_core(configured_core);
    let actual_target = std::env::var("TARGET").unwrap();
    if expected_target != actual_target {
        error!(
            "The selected core '{}' requires the target '{}', but the selected target is '{}'",
            configured_core, expected_target, actual_target
        );
    }
}