opentitanlib/transport/verilator/
gpio.rs1use anyhow::{Context, Result, ensure};
6use std::cell::{Cell, RefCell};
7use std::collections::HashMap;
8use std::fs::{File, OpenOptions};
9use std::io::{self, ErrorKind, Read, Write};
10use std::rc::Rc;
11use std::time::Duration;
12
13use crate::io::gpio::{GpioError, GpioPin, PinMode, PullMode};
14use crate::transport::TransportError;
15use crate::transport::verilator::transport::Inner;
16use crate::util::file;
17
18pub struct VerilatorGpioPin {
19 inner: Rc<RefCell<Inner>>,
20 pinname: u8,
21 out_value: Cell<bool>,
22 pinmode: Cell<PinMode>,
23 pullmode: Cell<PullMode>,
24}
25
26impl VerilatorGpioPin {
27 pub(crate) fn new(inner: Rc<RefCell<Inner>>, pinname: u8) -> Rc<Self> {
28 Rc::new(VerilatorGpioPin {
29 inner,
30 pinname,
31 out_value: Cell::new(false),
32 pinmode: Cell::new(PinMode::PushPull),
33 pullmode: Cell::new(PullMode::None),
34 })
35 }
36
37 fn set(&self) -> Result<()> {
38 let mut inner = self.inner.borrow_mut();
39 inner.gpio.set(
40 self.pinname,
41 self.out_value.get(),
42 self.pinmode.get(),
43 self.pullmode.get(),
44 )
45 }
46}
47
48impl GpioPin for VerilatorGpioPin {
49 fn read(&self) -> Result<bool> {
50 let mut inner = self.inner.borrow_mut();
51 inner.gpio.get(self.pinname)
52 }
53
54 fn write(&self, value: bool) -> Result<()> {
55 self.out_value.set(value);
56 self.set()
57 }
58
59 fn set_mode(&self, mode: PinMode) -> Result<()> {
60 self.pinmode.set(mode);
61 self.set()
62 }
63
64 fn set_pull_mode(&self, mode: PullMode) -> Result<()> {
65 self.pullmode.set(mode);
66 self.set()
67 }
68
69 fn set(
71 &self,
72 mode: Option<PinMode>,
73 value: Option<bool>,
74 pull: Option<PullMode>,
75 analog_value: Option<f32>,
76 ) -> Result<()> {
77 if analog_value.is_some() {
78 return Err(TransportError::UnsupportedOperation.into());
79 }
80 if let Some(mode) = mode {
81 self.pinmode.set(mode);
82 }
83 if let Some(pull) = pull {
84 self.pullmode.set(pull);
85 }
86 if let Some(value) = value {
87 self.out_value.set(value);
88 }
89 self.set()
90 }
91}
92
93pub(crate) struct GpioInner {
94 read: File,
95 write: File,
96 rval: u32,
97 pub pins: HashMap<u8, Rc<dyn GpioPin>>,
98}
99
100impl GpioInner {
101 pub fn new(read: &str, write: &str) -> Result<Self> {
102 let read = OpenOptions::new().read(true).open(read)?;
103 let write = OpenOptions::new().write(true).open(write)?;
104 Ok(GpioInner {
105 read,
106 write,
107 rval: 0,
108 pins: HashMap::default(),
109 })
110 }
111
112 fn read_pipe(&mut self) -> Result<()> {
113 loop {
114 match file::wait_read_timeout(&self.read, Duration::ZERO) {
115 Ok(()) => {
116 let mut buf = [0u8; 33];
117 let n = self.read.read(&mut buf).context("GPIO read error")?;
118 ensure!(
119 n == buf.len(),
120 GpioError::Generic(format!(
121 "Invalid gpio read length from simulator: {}",
122 n
123 ))
124 );
125 for (i, val) in buf.iter().enumerate() {
126 self.rval = match val {
127 b'0' => self.rval & !(1 << (31 - i)),
128 b'1' => self.rval | 1 << (31 - i),
129 b'\n' | b'X' => self.rval,
130 _ => {
131 return Err(GpioError::Generic(format!(
132 "Invalid gpio status from simulator: {:?}",
133 std::str::from_utf8(&buf)
134 ))
135 .into());
136 }
137 };
138 }
139 }
140 Err(e) => {
141 if let Some(ioerr) = e.downcast_ref::<io::Error>()
142 && ioerr.kind() == ErrorKind::TimedOut
143 {
144 break;
145 }
146 return Err(e).context("GPIO read error");
147 }
148 }
149 }
150 Ok(())
151 }
152
153 fn get(&mut self, index: u8) -> Result<bool> {
154 self.read_pipe()?;
155 Ok(self.rval & (1 << index) != 0)
156 }
157
158 fn set(&mut self, index: u8, val: bool, mode: PinMode, weak_pull: PullMode) -> Result<()> {
159 let code = match (mode, val, weak_pull) {
160 (PinMode::PushPull, true, _) => "h",
161 (PinMode::PushPull, false, _) => "l",
162 (PinMode::OpenDrain, false, _) => "l",
163 (_, _, PullMode::PullUp) => "wh",
165 (_, _, PullMode::PullDown) => "wl",
166 (_, _, PullMode::None) => "wl", };
168 let command = format!("{}{}\n", code, index);
169 self.write
170 .write(command.as_bytes())
171 .context("GPIO write error")?;
172 Ok(())
173 }
174}