blob: 6b0a6c785971ea6295e96177ad3812038ddfc63c [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
use anyhow::Result;
use lazy_static::lazy_static;
use std::collections::HashMap;
use std::time::Duration;
use structopt::StructOpt;
use opentitanlib::app::TransportWrapper;
use opentitanlib::io::gpio::PinMode;
use opentitanlib::io::uart::Uart;
use opentitanlib::test_utils::gpio::{GpioGet, GpioSet};
use opentitanlib::test_utils::init::InitializeTest;
use opentitanlib::test_utils::pinmux_config::PinmuxConfig;
use opentitanlib::uart::console::UartConsole;
use opentitanlib::{collection, execute_test};
use opentitanlib::chip::earlgrey::{PinmuxInsel, PinmuxMioOut, PinmuxOutsel, PinmuxPeripheralIn};
#[derive(Debug, StructOpt)]
struct Opts {
#[structopt(flatten)]
init: InitializeTest,
#[structopt(
long, parse(try_from_str=humantime::parse_duration),
default_value = "600s",
help = "Console receive timeout",
)]
timeout: Duration,
}
struct Config {
input: HashMap<PinmuxPeripheralIn, PinmuxInsel>,
output: HashMap<PinmuxMioOut, PinmuxOutsel>,
}
lazy_static! {
static ref CONFIG: HashMap<&'static str, Config> = collection! {
"verilator" => Config {
input: collection! {
PinmuxPeripheralIn::GpioGpio0 => PinmuxInsel::Iob6,
PinmuxPeripheralIn::GpioGpio1 => PinmuxInsel::Iob7,
PinmuxPeripheralIn::GpioGpio2 => PinmuxInsel::Iob8,
PinmuxPeripheralIn::GpioGpio3 => PinmuxInsel::Iob9,
PinmuxPeripheralIn::GpioGpio4 => PinmuxInsel::Iob10,
PinmuxPeripheralIn::GpioGpio5 => PinmuxInsel::Iob11,
PinmuxPeripheralIn::GpioGpio6 => PinmuxInsel::Iob12,
PinmuxPeripheralIn::GpioGpio7 => PinmuxInsel::Ior5,
PinmuxPeripheralIn::GpioGpio8 => PinmuxInsel::Ior6,
PinmuxPeripheralIn::GpioGpio9 => PinmuxInsel::Ior7,
// IOR8-9 aren't MIOs.
PinmuxPeripheralIn::GpioGpio10 => PinmuxInsel::Ior10,
PinmuxPeripheralIn::GpioGpio11 => PinmuxInsel::Ior11,
PinmuxPeripheralIn::GpioGpio12 => PinmuxInsel::Ior12,
PinmuxPeripheralIn::GpioGpio13 => PinmuxInsel::Ior13,
},
output: collection! {
PinmuxMioOut::Iob6 => PinmuxOutsel::GpioGpio0,
PinmuxMioOut::Iob7 => PinmuxOutsel::GpioGpio1,
PinmuxMioOut::Iob8 => PinmuxOutsel::GpioGpio2,
PinmuxMioOut::Iob9 => PinmuxOutsel::GpioGpio3,
PinmuxMioOut::Iob10 => PinmuxOutsel::GpioGpio4,
PinmuxMioOut::Iob11 => PinmuxOutsel::GpioGpio5,
PinmuxMioOut::Iob12 => PinmuxOutsel::GpioGpio6,
PinmuxMioOut::Ior5 => PinmuxOutsel::GpioGpio7,
PinmuxMioOut::Ior6 => PinmuxOutsel::GpioGpio8,
PinmuxMioOut::Ior7 => PinmuxOutsel::GpioGpio9,
// IOR8-9 aren't MIOs.
PinmuxMioOut::Ior10 => PinmuxOutsel::GpioGpio10,
PinmuxMioOut::Ior11 => PinmuxOutsel::GpioGpio11,
PinmuxMioOut::Ior12 => PinmuxOutsel::GpioGpio12,
PinmuxMioOut::Ior13 => PinmuxOutsel::GpioGpio13,
},
},
"hyperdebug" => Config {
input: collection! {
// The commented lines represent multi-fuction pins. These will
// be added back in when the hyperdebug firmware can set these
// multifunction pins into GPIO mode.
//PinmuxPeripheralIn::GpioGpio0 => PinmuxInsel::Ioa0, // UART4
//PinmuxPeripheralIn::GpioGpio1 => PinmuxInsel::Ioa1, // UART4
PinmuxPeripheralIn::GpioGpio2 => PinmuxInsel::Ioa2,
PinmuxPeripheralIn::GpioGpio3 => PinmuxInsel::Ioa3,
//PinmuxPeripheralIn::GpioGpio4 => PinmuxInsel::Ioa4, // UART5
//PinmuxPeripheralIn::GpioGpio5 => PinmuxInsel::Ioa5, // UART5
PinmuxPeripheralIn::GpioGpio6 => PinmuxInsel::Ioa6,
//PinmuxPeripheralIn::GpioGpio7 => PinmuxInsel::Ioa7, // I2C1
//PinmuxPeripheralIn::GpioGpio8 => PinmuxInsel::Ioa8, // I2C1
//PinmuxPeripheralIn::GpioGpio9 => PinmuxInsel::Iob4, // UART3
//PinmuxPeripheralIn::GpioGpio10 => PinmuxInsel::Iob5, // UART3
PinmuxPeripheralIn::GpioGpio11 => PinmuxInsel::Iob6,
PinmuxPeripheralIn::GpioGpio12 => PinmuxInsel::Ioc0,
PinmuxPeripheralIn::GpioGpio13 => PinmuxInsel::Ioc1,
PinmuxPeripheralIn::GpioGpio14 => PinmuxInsel::Ioc2,
PinmuxPeripheralIn::GpioGpio15 => PinmuxInsel::Ioc5,
PinmuxPeripheralIn::GpioGpio16 => PinmuxInsel::Ioc6,
PinmuxPeripheralIn::GpioGpio17 => PinmuxInsel::Ioc10,
PinmuxPeripheralIn::GpioGpio18 => PinmuxInsel::Ioc11,
PinmuxPeripheralIn::GpioGpio19 => PinmuxInsel::Ioc12,
PinmuxPeripheralIn::GpioGpio20 => PinmuxInsel::Ior5,
PinmuxPeripheralIn::GpioGpio21 => PinmuxInsel::Ior6,
PinmuxPeripheralIn::GpioGpio22 => PinmuxInsel::Ior7,
// IOR8-9 aren't MIOs.
PinmuxPeripheralIn::GpioGpio25 => PinmuxInsel::Ior10,
PinmuxPeripheralIn::GpioGpio26 => PinmuxInsel::Ior11,
PinmuxPeripheralIn::GpioGpio27 => PinmuxInsel::Ior12,
PinmuxPeripheralIn::GpioGpio28 => PinmuxInsel::Ior13,
},
output: collection! {
// The commented lines represent multi-fuction pins. These will
// be added back in when the hyperdebug firmware can set these
// multifunction pins into GPIO mode.
//PinmuxMioOut::Ioa0 => PinmuxOutsel::GpioGpio0, // UART4
//PinmuxMioOut::Ioa1 => PinmuxOutsel::GpioGpio1, // UART4
PinmuxMioOut::Ioa2 => PinmuxOutsel::GpioGpio2,
PinmuxMioOut::Ioa3 => PinmuxOutsel::GpioGpio3,
//PinmuxMioOut::Ioa4 => PinmuxOutsel::GpioGpio4, // UART5
//PinmuxMioOut::Ioa5 => PinmuxOutsel::GpioGpio5, // UART5
PinmuxMioOut::Ioa6 => PinmuxOutsel::GpioGpio6,
//PinmuxMioOut::Ioa7 => PinmuxOutsel::GpioGpio7, // I2C1
//PinmuxMioOut::Ioa8 => PinmuxOutsel::GpioGpio8, // I2C1
//PinmuxMioOut::Iob4 => PinmuxOutsel::GpioGpio9, // UART3
//PinmuxMioOut::Iob5 => PinmuxOutsel::GpioGpio10, // UART3
PinmuxMioOut::Iob6 => PinmuxOutsel::GpioGpio11,
PinmuxMioOut::Ioc0 => PinmuxOutsel::GpioGpio12,
PinmuxMioOut::Ioc1 => PinmuxOutsel::GpioGpio13,
PinmuxMioOut::Ioc2 => PinmuxOutsel::GpioGpio14,
PinmuxMioOut::Ioc5 => PinmuxOutsel::GpioGpio15,
PinmuxMioOut::Ioc6 => PinmuxOutsel::GpioGpio16,
PinmuxMioOut::Ioc10 => PinmuxOutsel::GpioGpio17,
PinmuxMioOut::Ioc11 => PinmuxOutsel::GpioGpio18,
PinmuxMioOut::Ioc12 => PinmuxOutsel::GpioGpio19,
PinmuxMioOut::Ior5 => PinmuxOutsel::GpioGpio20,
PinmuxMioOut::Ior6 => PinmuxOutsel::GpioGpio21,
PinmuxMioOut::Ior7 => PinmuxOutsel::GpioGpio22,
// IOR8-9 aren't MIOs.
PinmuxMioOut::Ior10 => PinmuxOutsel::GpioGpio25,
PinmuxMioOut::Ior11 => PinmuxOutsel::GpioGpio26,
PinmuxMioOut::Ior12 => PinmuxOutsel::GpioGpio27,
PinmuxMioOut::Ior13 => PinmuxOutsel::GpioGpio28,
},
},
};
}
fn write_all_verify(
transport: &TransportWrapper,
uart: &dyn Uart,
value: u32,
config: &HashMap<PinmuxMioOut, PinmuxOutsel>,
) -> Result<()> {
let mask = config.values().fold(0, |acc, v| {
let i = u32::from(*v) - u32::from(PinmuxOutsel::GpioGpio0);
acc | 1u32 << i
});
let masked_value = value & mask;
log::info!(
"write & verify pattern: {:08x} & {:08x} -> {:08x}",
value,
mask,
masked_value
);
if masked_value == 0 {
log::info!(
"skipping: {:08x} has no bits in common with the pinmux configuration",
value
);
} else {
GpioSet::write_all(uart, masked_value)?;
let mut result = 0u32;
for (k, v) in config.iter() {
let n = u32::from(transport.gpio_pin(&k.to_string())?.read()?);
let i = u32::from(*v) - u32::from(PinmuxOutsel::GpioGpio0);
result |= n << i;
}
log::info!("result = {:08x}", result);
assert_eq!(masked_value, result);
}
Ok(())
}
fn read_all_verify(
transport: &TransportWrapper,
uart: &dyn Uart,
value: u32,
config: &HashMap<PinmuxPeripheralIn, PinmuxInsel>,
) -> Result<()> {
let mask = config.keys().fold(0, |acc, k| {
let i = u32::from(*k) - u32::from(PinmuxPeripheralIn::GpioGpio0);
acc | 1u32 << i
});
let masked_value = value & mask;
log::info!(
"read & verify pattern: {:08x} & {:08x} -> {:08x}",
value,
mask,
masked_value
);
if masked_value == 0 {
log::info!(
"skipping: {:08x} has no bits in common with the pinmux configuration",
value
);
} else {
for (k, v) in config.iter() {
let i = u32::from(*k) - u32::from(PinmuxPeripheralIn::GpioGpio0);
transport
.gpio_pin(&v.to_string())?
.write((masked_value >> i) & 1 != 0)?;
}
let result = GpioGet::read_all(uart)? & mask;
log::info!("result = {:08x}", result);
assert_eq!(masked_value, result);
}
Ok(())
}
fn test_gpio_outputs(opts: &Opts, transport: &TransportWrapper) -> Result<()> {
let uart = transport.uart("console")?;
log::info!(
"Configuring pinmux for {:?}",
opts.init.backend_opts.interface
);
let config = CONFIG
.get(opts.init.backend_opts.interface.as_str())
.expect("interface");
PinmuxConfig::configure(&*uart, None, Some(&config.output))?;
log::info!("Configuring debugger GPIOs as inputs");
// The outputs (with respect to pinmux config) correspond to the input pins on the debug board.
for pin in config.output.keys() {
transport
.gpio_pin(&pin.to_string())?
.set_mode(PinMode::Input)?;
}
log::info!("Enabling outputs on the DUT");
GpioSet::set_enabled_all(&*uart, 0xFFFFFFFF)?;
write_all_verify(transport, &*uart, 0x5555_5555, &config.output)?;
write_all_verify(transport, &*uart, 0xAAAA_AAAA, &config.output)?;
for i in 0..32 {
write_all_verify(transport, &*uart, 1 << i, &config.output)?;
}
Ok(())
}
fn test_gpio_inputs(opts: &Opts, transport: &TransportWrapper) -> Result<()> {
let uart = transport.uart("console")?;
log::info!(
"Configuring pinmux for {:?}",
opts.init.backend_opts.interface
);
let config = CONFIG
.get(opts.init.backend_opts.interface.as_str())
.expect("interface");
PinmuxConfig::configure(&*uart, Some(&config.input), None)?;
log::info!("Configuring debugger GPIOs as outputs");
// The inputs (with respect to pinmux config) correspond to the output pins on the debug board.
for pin in config.input.values() {
transport
.gpio_pin(&pin.to_string())?
.set_mode(PinMode::PushPull)?;
}
log::info!("Disabling outputs on the DUT");
GpioSet::set_enabled_all(&*uart, 0x0)?;
read_all_verify(transport, &*uart, 0x5555_5555, &config.input)?;
read_all_verify(transport, &*uart, 0xAAAA_AAAA, &config.input)?;
for i in 0..32 {
read_all_verify(transport, &*uart, 1 << i, &config.input)?;
}
Ok(())
}
fn main() -> Result<()> {
let opts = Opts::from_args();
opts.init.init_logging();
let transport = opts.init.init_target()?;
let uart = transport.uart("console")?;
uart.set_flow_control(true)?;
let _ = UartConsole::wait_for(&*uart, r"Running [^\r\n]*", opts.timeout)?;
execute_test!(test_gpio_outputs, &opts, &transport);
execute_test!(test_gpio_inputs, &opts, &transport);
Ok(())
}