opentitanlib/transport/ti50emulator/
mod.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::{Context, Result};
6use std::cell::RefCell;
7use std::collections::HashMap;
8use std::fs;
9use std::path::{Path, PathBuf};
10use std::process;
11use std::rc::{Rc, Weak};
12use std::time::{SystemTime, UNIX_EPOCH};
13use std::vec::Vec;
14
15use crate::io::emu::Emulator;
16use crate::io::gpio::GpioPin;
17use crate::io::i2c::Bus;
18use crate::io::spi::Target;
19use crate::io::uart::Uart;
20use crate::transport::{
21    Capabilities, Capability, Transport, TransportError, TransportInterfaceType,
22};
23
24mod emu;
25mod gpio;
26mod i2c;
27mod uart;
28
29use crate::transport::ti50emulator::emu::{EmulatorImpl, EmulatorProcess, ResetPin};
30use crate::transport::ti50emulator::gpio::Ti50GpioPin;
31use crate::transport::ti50emulator::i2c::Ti50I2cBus;
32use crate::transport::ti50emulator::uart::Ti50Uart;
33
34pub struct Ti50Emulator {
35    /// Mapping of GPIO pins handles to their symbolic names.
36    gpio_map: HashMap<String, Rc<dyn GpioPin>>,
37    /// Mapping of I2C handles to their symbolic names.
38    i2c_map: HashMap<String, Rc<dyn Bus>>,
39    /// Mapping of UART handles to their symbolic names.
40    uart_map: HashMap<String, Rc<dyn Uart>>,
41    /// Struct implementing the Emulator trait
42    emu: Rc<EmulatorImpl>,
43}
44
45impl Ti50Emulator {
46    /// Create new instance of [`Ti50Emulator`] based on provided parameters.
47    pub fn open(
48        executable_directory: &Path,
49        executable: &str,
50        instance_prefix: &str,
51    ) -> anyhow::Result<Self> {
52        let tstamp = SystemTime::now().duration_since(UNIX_EPOCH)?;
53        let instance_name = format!(
54            "{}_{}_{}_{}",
55            instance_prefix,
56            process::id(),
57            tstamp.as_secs(),
58            tstamp.as_nanos()
59        );
60
61        let mut instance_directory = PathBuf::from("/tmp");
62        instance_directory.push(&instance_name);
63
64        log::info!("Initializing Ti50Emulator instance: {}", instance_name);
65        fs::create_dir(&instance_directory).context("Falied to create instance directory")?;
66
67        let process = EmulatorProcess::init(&instance_directory, executable_directory, executable)?;
68
69        let conf = process.get_configurations()?;
70
71        let inner = Rc::new(Inner {
72            instance_directory,
73            process: RefCell::new(process),
74            gpio_map: RefCell::default(),
75            uarts: RefCell::default(),
76        });
77
78        let mut gpio_map: HashMap<String, Rc<dyn GpioPin>> = HashMap::new();
79        let mut i2c_map = HashMap::new();
80        let mut uart_map = HashMap::new();
81
82        let reset_pin = ResetPin::open(&inner)?;
83        gpio_map.insert("RESET".to_string(), Rc::new(reset_pin));
84        for (name, state) in conf.gpio.iter() {
85            inner
86                .gpio_map
87                .borrow_mut()
88                .insert(name.to_string(), gpio::GpioConfiguration::default());
89            let gpio: Rc<dyn GpioPin> = Rc::new(Ti50GpioPin::open(&inner, name, *state)?);
90            gpio_map.insert(name.to_uppercase(), Rc::clone(&gpio));
91        }
92        for (name, path) in conf.uart.iter() {
93            let uart: Rc<Ti50Uart> = Rc::new(Ti50Uart::open(&inner, path)?);
94            uart_map.insert(name.to_uppercase(), Rc::clone(&uart) as Rc<dyn Uart>);
95            inner.uarts.borrow_mut().push(Rc::downgrade(&uart));
96        }
97        for (name, path) in conf.i2c.iter() {
98            let i2c: Rc<dyn Bus> = Rc::new(Ti50I2cBus::open(&inner, path)?);
99            i2c_map.insert(name.to_uppercase(), Rc::clone(&i2c));
100        }
101        let ti50_emu = Ti50Emulator {
102            gpio_map,
103            i2c_map,
104            uart_map,
105            emu: Rc::new(EmulatorImpl::open(&inner)?),
106        };
107        Ok(ti50_emu)
108    }
109}
110
111impl Drop for Inner {
112    fn drop(&mut self) {
113        log::info!(
114            "Clenup Ti50Emulator instance directory: {}",
115            self.instance_directory.display()
116        );
117        if let Err(e) = fs::remove_dir_all(&self.instance_directory) {
118            log::error!("Can't remove instance directory error: {}", e)
119        }
120    }
121}
122
123/// Structure representing internal state of emulator
124pub struct Inner {
125    /// Path of parent directory representing `Ti50Emulator` instance.
126    instance_directory: PathBuf,
127    /// SubProcess instance
128    process: RefCell<EmulatorProcess>,
129    /// Current state of host drive of all GPIOs
130    gpio_map: RefCell<HashMap<String, gpio::GpioConfiguration>>,
131    uarts: RefCell<Vec<Weak<Ti50Uart>>>,
132}
133
134impl Inner {}
135
136/// Implementation of the Transport trait backed based on TockOS HostEmulation port.
137impl Transport for Ti50Emulator {
138    fn capabilities(&self) -> Result<Capabilities> {
139        Ok(Capabilities::new(
140            Capability::UART | Capability::GPIO | Capability::I2C | Capability::EMULATOR,
141        ))
142    }
143
144    // Returns one of existing SPI instance.
145    fn spi(&self, instance: &str) -> Result<Rc<dyn Target>> {
146        Err(TransportError::InvalidInstance(
147            TransportInterfaceType::Spi,
148            instance.to_string(),
149        ))?
150    }
151
152    // Returns one of existing I2C instance.
153    fn i2c(&self, instance: &str) -> Result<Rc<dyn Bus>> {
154        Ok(Rc::clone(self.i2c_map.get(instance).ok_or_else(|| {
155            TransportError::InvalidInstance(TransportInterfaceType::I2c, instance.to_string())
156        })?))
157    }
158
159    // Returns one of existing UART instance.
160    fn uart(&self, instance: &str) -> Result<Rc<dyn Uart>> {
161        Ok(Rc::clone(self.uart_map.get(instance).ok_or_else(|| {
162            TransportError::InvalidInstance(TransportInterfaceType::Uart, instance.to_string())
163        })?))
164    }
165
166    // Returns one of existing GPIO pin instance.
167    fn gpio_pin(&self, pinname: &str) -> Result<Rc<dyn GpioPin>> {
168        Ok(Rc::clone(self.gpio_map.get(pinname).ok_or_else(|| {
169            TransportError::InvalidInstance(TransportInterfaceType::Gpio, pinname.to_string())
170        })?))
171    }
172
173    // Create Emulator instance, or return one from a cache of previously created instances.
174    fn emulator(&self) -> Result<Rc<dyn Emulator>> {
175        Ok(self.emu.clone())
176    }
177}