memea/config.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! Configuration management for MemEA memory peripheral estimation.
//!
//! This module provides functionality to read and manage memory configurations
//! from YAML files. Each configuration specifies memory array parameters,
//! cell types, voltages, and ADC settings used for peripheral estimation.
use serde::Deserialize;
use std::fs;
use std::io::BufReader;
use std::{collections::HashMap, path::PathBuf};
use thiserror::Error;
use crate::{errorln, Float, MemeaError};
/// A collection of memory configurations indexed by name.
type Configs = HashMap<String, Config>;
/// Errors that can occur during configuration processing.
#[derive(Debug, Error)]
pub enum ConfigError {
/// Indicates that a required configuration option was not found.
#[error("Cannot find option in config: {0}")]
MissingOption(String),
}
/// Represents one memory configuration for peripheral estimation.
///
/// This struct is typically deserialized from YAML or JSON using `serde`. It
/// contains both essential and optional parameters that control the peripheral
/// estimation process.
///
/// # Examples
///
/// ```yaml
/// name: 64-64
/// n: 128
/// m: 64
/// bl: [1, 2, 0]
/// wl: [4, 2.5, 0, 1]
/// well: [0, 4]
/// cell: 1FeFET_100
/// enob: 1
/// fs: 1e9
/// adcs: 64
/// ```
#[derive(Debug, Deserialize)]
pub struct Config {
/// Name of the configuration. If not supplied, the file path will be used.
pub name: Option<String>,
/// Number of rows in the memory array.
pub n: usize,
/// Number of columns in the memory array.
pub m: usize,
/// Memory cell type to use for estimation.
pub cell: String,
/// Bitline voltages
pub bl: Option<Vec<Float>>,
/// Wordline voltages
pub wl: Option<Vec<Float>>,
/// Voltages required for well biasing
pub well: Option<Vec<Float>>,
/// Number of downstream analog-to-digital converters.
pub adcs: Option<usize>,
/// Number of bits required for ADCs.
pub bits: Option<usize>,
/// Sampling rate of the ADCs in Hz.
pub fs: Option<Float>,
/// Additional configuration options as key-value pairs.
pub options: Option<HashMap<String, String>>,
}
/// Deserializes a configuration from a YAML file.
///
/// # Arguments
/// * `filename` - Path of the YAML file to read
///
/// # Returns
/// * `Ok(Config)` - Successfully parsed configuration
/// * `Err(MemeaError)` - File I/O error or YAML parsing error
///
/// # Examples
/// ```no_run
/// use std::path::PathBuf;
/// # use memea::config::read;
///
/// let config_path = PathBuf::from("config.yaml");
/// let config = read(&config_path).expect("Failed to read config");
/// ```
fn read(filename: &std::path::PathBuf) -> Result<Config, MemeaError> {
let file = fs::File::open(filename)?;
let rdr = BufReader::new(file);
let config: Config = serde_yaml::from_reader(rdr)?;
Ok(config)
}
/// Reads multiple configuration files and returns them indexed by name.
///
/// This function attempts to read all provided configuration files. If a file
/// fails to parse, an error is logged and that file is skipped. The resulting
/// HashMap uses either the configured name or the file path as the key.
///
/// # Arguments
/// * `paths` - Vector of configuration file paths to read
///
/// # Returns
/// * `HashMap<String, Config>` - Successfully parsed configurations indexed by name
///
/// # Examples
/// ```no_run
/// use std::path::PathBuf;
/// # use memea::config::read_all;
///
/// let paths = vec![
/// PathBuf::from("config1.yaml"),
/// PathBuf::from("config2.yaml"),
/// ];
/// let configs = read_all(&paths);
/// println!("Loaded {} configurations", configs.len());
/// ```
pub fn read_all(paths: &Vec<PathBuf>) -> Configs {
let mut configs: Configs = HashMap::new();
for c in paths {
match read(c) {
Ok(r) => {
let name = match &r.name {
Some(s) => s.clone(),
None => c.to_string_lossy().into(),
};
configs.insert(name, r);
}
Err(e) => errorln!("Failed to read config {:?} ({})", &c, e),
}
}
configs
}