Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/bin/bmputil-cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,9 +439,13 @@ fn power_command(cli_args: &CliArguments) -> Result<()>
let device = results.pop_single("power").map_err(|kind| kind.error())?;
let remote = device.bmd_serial_interface()?.remote()?;

let power = remote.get_target_power_state()?;
match remote.get_target_power_state() {
Ok(power) => info!("Device target power state: {}", power),
Err(e) => warn!("Failed to retrieve target power state: {}", e),
};

info!("Device target power state: {}", power);
let voltage = remote.get_nrst_voltage()?;
info!("Target voltage: {}V", voltage);

Ok(())
}
Expand Down
109 changes: 60 additions & 49 deletions src/serial/bmd_rsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,11 @@ pub struct BmdRspInterface
handle: File,
protocol_version: ProtocolVersion,

read_buffer: [u8; REMOTE_MAX_MSG_SIZE],
read_buffer: [u8; RemoteResponse::MAX_MSG_SIZE],
read_buffer_fullness: usize,
read_buffer_offset: usize,
}

const REMOTE_START: &str = "+#!GA#";
const REMOTE_HL_CHECK: &str = "!HC#";

impl BmdRspInterface
{
pub fn from_path(serial_port: &Path) -> Result<Self>
Expand All @@ -35,7 +32,7 @@ impl BmdRspInterface
let handle = File::options().read(true).write(true).open(serial_port)?;

// Construct an interface object
let mut result = Self {
let mut interface = Self {
handle,
// We start out by not knowing what version of protocol the probe talks
protocol_version: ProtocolVersion::Unknown,
Expand All @@ -44,20 +41,66 @@ impl BmdRspInterface
// probe responses, being mindful that there's no good way to find out
// how much data is waiting for us from the probe, so it's this or use
// a read call a byte, which is extremely expensive!
read_buffer: [0; REMOTE_MAX_MSG_SIZE],
read_buffer: [0; RemoteResponse::MAX_MSG_SIZE],
read_buffer_fullness: 0,
read_buffer_offset: 0,
};

// Call the OS-specific handle configuration function to ready
// the interface handle for use with the remote serial protocol
result.init_handle()?;
interface.init_handle()?;

// Start remote protocol communications with the probe
result.buffer_write(REMOTE_START)?;
let buffer = result.buffer_read()?;
Self::start_probe_communication(&mut interface)?;

// Next, ask the probe for its protocol version number.
// For historical reasons this is part of the "high level" protocol set, but is
// actually a general request.
interface.protocol_version = Self::protocol_version(&mut interface)?;
trace!("Probe talks BMD RSP {}", interface.protocol_version);

// Now the object is ready to go, return it to the caller
Ok(interface)
}

fn protocol_version(interface: &mut BmdRspInterface) -> Result<ProtocolVersion>
{
interface.buffer_write(RemoteCommands::HL_CHECK)?;
let buffer = interface.buffer_read()?;

// Check for communication failures, it should respond with at least 1 character
let (first, rest) = buffer
.split_at_checked(1)
.ok_or_else(|| eyre!("Probe failed to respond at all to protocol version request"))?;

match (first.as_bytes()[0], rest) {
// If the request failed by way of a not implemented response, we're on a v0 protocol probe
(RemoteResponse::RESP_NOTSUP, _) => Ok(ProtocolVersion::V0),
(RemoteResponse::RESP_OK, rest) => {
// Parse out the version number from the response
let version = decode_response(&rest, 8);
// Then decode/translate that to a protocol version enum value
match version {
// Protocol version number 0 coresponds to an enchanced v0 probe protocol ("v0+")
0 => Ok(ProtocolVersion::V0Plus),
1 => Ok(ProtocolVersion::V1),
2 => Ok(ProtocolVersion::V2),
3 => Ok(ProtocolVersion::V3),
4 => Ok(ProtocolVersion::V4),
_ => Err(eyre!("Unknown remote protocol version {}", version)),
}
},
// If the probe responded with anything other than OK or not supported, we're done
_ => Err(eyre!("Probe responded improperly to protocol version request with {}", buffer)),
}
}

fn start_probe_communication(interface: &mut BmdRspInterface) -> Result<()>
{
interface.buffer_write(RemoteCommands::START)?;
let buffer = interface.buffer_read()?;
// Check if that failed for any reason
if buffer.is_empty() || buffer.as_bytes()[0] != REMOTE_RESP_OK {
if buffer.is_empty() || buffer.as_bytes()[0] != RemoteResponse::RESP_OK {
let message = if buffer.len() > 1 {
&buffer[1..]
} else {
Expand All @@ -66,41 +109,9 @@ impl BmdRspInterface
return Err(eyre!("Remote protocol startup failed, error {}", message));
}
// It did not, grand - we now have the firmware version string, so log it
debug!("Remote is {}", &buffer[1..]);

// Next, ask the probe for its protocol version number.
// For historical reasons this is part of the "high level" protocol set, but is
// actually a general request.
result.buffer_write(REMOTE_HL_CHECK)?;
let buffer = result.buffer_read()?;
// Check for communication failures
if buffer.is_empty() {
return Err(eyre!("Probe failed to respond at all to protocol version request"));
} else if buffer.as_bytes()[0] != REMOTE_RESP_OK && buffer.as_bytes()[0] != REMOTE_RESP_NOTSUP {
// If the probe responded with anything other than OK or not supported, we're done
return Err(eyre!("Probe responded improperly to protocol version request with {}", buffer));
}
// If the request failed by way of a not implemented response, we're on a v0 protocol probe
if buffer.as_bytes()[0] == REMOTE_RESP_NOTSUP {
result.protocol_version = ProtocolVersion::V0;
} else {
// Parse out the version number from the response
let version = decode_response(&buffer[1..], 8);
// Then decode/translate that to a protocol version enum value
result.protocol_version = match version {
// Protocol version number 0 coresponds to an enchanced v0 probe protocol ("v0+")
0 => ProtocolVersion::V0Plus,
1 => ProtocolVersion::V1,
2 => ProtocolVersion::V2,
3 => ProtocolVersion::V3,
4 => ProtocolVersion::V4,
_ => return Err(eyre!("Unknown remote protocol version {}", version)),
};
}
trace!("Probe talks BMD RSP {}", result.protocol_version);

// Now the object is ready to go, return it to the caller
Ok(result)
let firmware_version = &buffer[1..];
debug!("Remote is {}", firmware_version);
Ok(())
}

/// Extract the remote protocol object to use to talk with this probe
Expand All @@ -124,7 +135,7 @@ impl BmdRspInterface
{
// First drain the buffer till we see a start-of-response byte
let mut response = 0;
while response != REMOTE_RESP {
while response != RemoteResponse::RESP {
if self.read_buffer_offset == self.read_buffer_fullness {
self.read_more_data()?;
}
Expand All @@ -133,7 +144,7 @@ impl BmdRspInterface
}

// Now collect the response
let mut buffer = [0u8; REMOTE_MAX_MSG_SIZE];
let mut buffer = [0u8; RemoteResponse::MAX_MSG_SIZE];
let mut offset = 0;
while offset < buffer.len() {
// Check if we need more data or should use what's in the buffer already
Expand All @@ -145,7 +156,7 @@ impl BmdRspInterface
while self.read_buffer_offset + response_length < self.read_buffer_fullness &&
offset + response_length < buffer.len()
{
if self.read_buffer[self.read_buffer_offset + response_length] == REMOTE_EOM {
if self.read_buffer[self.read_buffer_offset + response_length] == RemoteResponse::EOM {
response_length += 1;
break;
}
Expand All @@ -158,7 +169,7 @@ impl BmdRspInterface
self.read_buffer_offset += response_length;
offset += response_length - 1;
// If this was because of REMOTE_EOM, return
if buffer[offset] == REMOTE_EOM {
if buffer[offset] == RemoteResponse::EOM {
buffer[offset] = 0;
let result = unsafe { String::from_utf8_unchecked(buffer[..offset].to_vec()) };
debug!("BMD RSP read: {}", result);
Expand Down
60 changes: 43 additions & 17 deletions src/serial/remote/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,49 @@ mod protocol_v3;
mod protocol_v4;
pub mod riscv_debug;

/// This is the max possible size of a remote protocol packet which a hard limitation of the
/// firmware on the probe - 1KiB is all the buffer that could be spared.
pub const REMOTE_MAX_MSG_SIZE: usize = 1024;
pub struct RemoteCommands;

/// Start of message marker for the protocol
pub const REMOTE_SOM: u8 = b'!';
/// End of message marker for the protocol
pub const REMOTE_EOM: u8 = b'#';
/// Response marker for the protocol
pub const REMOTE_RESP: u8 = b'&';
impl RemoteCommands
{
/// This command asks the probe if the reset pin is on
pub const GET_NRST: &str = "!Gz#";
/// This command asks the probe if power is being supplied to the target
pub const GET_TARGET_POWER_STATE: &str = "!Gp#";
/// This command asks the probe what it's protocol version is
pub const HL_CHECK: &str = "!HC#";
/// This command asks the probe to initialise JTAG comms to any connected targets
pub const JTAG_INIT: &str = "!JS#";
pub const NRST_TARGET_VOLTAGE: &str = "!GV#";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure what happened here, but this one isn't documented now and the constant is misnamed for what it does - this retrieves the target voltage.

/// This command asks the probe to the reset pin state
pub const SET_NRST: &str = "!GZ#";
/// This command asks the probe to set the power state to the target
pub const SET_TARGET_POWER_STATE: &str = "!GP#";
/// This command asks to start remote protocol communications with the probe
pub const START: &str = "+#!GA#";
}

pub struct RemoteResponse;

/// Probe response was okay and the data returned is valid
pub const REMOTE_RESP_OK: u8 = b'K';
/// Probe found an error with a request parameter
pub const REMOTE_RESP_PARERR: u8 = b'P';
/// Probe encountered an error executing the request
pub const REMOTE_RESP_ERR: u8 = b'E';
/// Probe does not support the request made
pub const REMOTE_RESP_NOTSUP: u8 = b'N';
impl RemoteResponse
{
/// End of message marker for the protocol
pub const EOM: u8 = b'#';
/// This is the max possible size of a remote protocol packet which a hard limitation of the
/// firmware on the probe - 1KiB is all the buffer that could be spared.
pub const MAX_MSG_SIZE: usize = 1024;
Comment on lines +55 to +57
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NB: this constant is not specific to the response - it sets the request max length too.

/// Response marker for the protocol
pub const RESP: u8 = b'&';
/// Probe encountered an error executing the request
pub const RESP_ERR: u8 = b'E';
/// Probe does not support the request made
pub const RESP_NOTSUP: u8 = b'N';
/// Probe response was okay and the data returned is valid
pub const RESP_OK: u8 = b'K';
/// Probe found an error with a request parameter
pub const RESP_PARERR: u8 = b'P';
/// Start of message marker for the protocol
pub const SOM: u8 = b'!';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is part of the request not response - same for EOM above. It's the RESP marker that indicates the start of a response.

}

pub type TargetAddr32 = u32;
pub type TargetAddr64 = u64;
Expand Down Expand Up @@ -112,6 +136,8 @@ pub trait BmdRemoteProtocol
fn supported_architectures(&self) -> Result<Option<TargetArchitecture>>;
fn supported_families(&self) -> Result<Option<TargetFamily>>;
fn get_target_power_state(&self) -> Result<bool>;
fn get_nrst_voltage(&self) -> Result<f32>;
fn get_nrst_val(&self) -> Result<bool>;
}

/// Types implementing this trait provide raw SWD access to targets over the BMD remote protocol
Expand Down
55 changes: 52 additions & 3 deletions src/serial/remote/protocol_v0.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// SPDX-FileCopyrightText: 2025 1BitSquared <[email protected]>
// SPDX-FileContributor: Written by Rachel Mant <[email protected]>
// SPDX-FileContributor: Modified by P-Storm <[email protected]>

use std::sync::{Arc, Mutex, MutexGuard};

Expand All @@ -11,7 +12,7 @@ use crate::serial::bmd_rsp::BmdRspInterface;
use crate::serial::remote::adi::{AdiV5AccessPort, AdiV5DebugPort};
use crate::serial::remote::{
Align, BmdAdiV5Protocol, BmdJtagProtocol, BmdRemoteProtocol, BmdRiscvProtocol, BmdSwdProtocol, JtagDev,
REMOTE_RESP_ERR, TargetAddr64, TargetArchitecture, TargetFamily,
RemoteCommands, RemoteResponse, TargetAddr64, TargetArchitecture, TargetFamily,
};

pub struct RemoteV0
Expand Down Expand Up @@ -83,7 +84,7 @@ impl BmdRemoteProtocol for RemoteV0
self.interface().buffer_write(REMOTE_JTAG_INIT)?;
let buffer = self.interface().buffer_read()?;
// If that failed for some reason, report it and abort
if buffer.is_empty() || buffer.as_bytes()[0] == REMOTE_RESP_ERR {
if buffer.is_empty() || buffer.as_bytes()[0] == RemoteResponse::RESP_ERR {
let message = if buffer.len() > 1 {
&buffer[1..]
} else {
Expand All @@ -102,7 +103,7 @@ impl BmdRemoteProtocol for RemoteV0
self.interface().buffer_write(REMOTE_SWD_INIT)?;
let buffer = self.interface().buffer_read()?;
// If that failed for some reason, report it and abort
if buffer.is_empty() || buffer.as_bytes()[0] == REMOTE_RESP_ERR {
if buffer.is_empty() || buffer.as_bytes()[0] == RemoteResponse::RESP_ERR {
let message = if buffer.len() > 1 {
&buffer[1..]
} else {
Expand Down Expand Up @@ -169,6 +170,44 @@ impl BmdRemoteProtocol for RemoteV0
{
Err(eyre!("Not supported"))
}

fn get_nrst_voltage(&self) -> Result<f32>
{
self.interface().buffer_write(RemoteCommands::NRST_TARGET_VOLTAGE)?;
let buffer = self.interface().buffer_read()?;

if buffer.is_empty() || buffer.as_bytes()[0] != RemoteResponse::RESP_OK {
return Err(eyre!("Target voltage request failed"));
}

if buffer.len() < 2 {
return Err(eyre!("Target voltage response is too short"));
}

let value = buffer
.get(1..buffer.len().saturating_sub(1))
.expect("Should have some value");

value
.parse::<f32>()
.map_err(|e| eyre!("Can't parse target voltage value to a float, input {}, reason: {}", value, e))
}

fn get_nrst_val(&self) -> Result<bool>
{
self.interface().buffer_write(RemoteCommands::GET_NRST)?;
let buffer = self.interface().buffer_read()?;

if buffer.is_empty() || buffer.as_bytes()[0] != RemoteResponse::RESP_OK {
return Err(eyre!("srst value request failed"));
}

if buffer.len() < 2 {
return Err(eyre!("srst value response is too short"));
}

Ok(buffer.as_bytes()[1] == b'1')
}
}

impl From<Arc<Mutex<BmdRspInterface>>> for RemoteV0Plus
Expand Down Expand Up @@ -253,6 +292,16 @@ impl BmdRemoteProtocol for RemoteV0Plus
{
self.0.get_target_power_state()
}

fn get_nrst_voltage(&self) -> Result<f32>
{
self.0.get_nrst_voltage()
}

fn get_nrst_val(&self) -> Result<bool>
{
self.0.get_nrst_val()
}
}

impl From<Arc<Mutex<BmdRspInterface>>> for RemoteV0JTAG
Expand Down
11 changes: 11 additions & 0 deletions src/serial/remote/protocol_v1.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// SPDX-FileCopyrightText: 2025 1BitSquared <[email protected]>
// SPDX-FileContributor: Written by Rachel Mant <[email protected]>
// SPDX-FileContributor: Modified by P-Storm <[email protected]>

use std::sync::{Arc, Mutex, MutexGuard};

Expand Down Expand Up @@ -115,6 +116,16 @@ impl BmdRemoteProtocol for RemoteV1
{
self.0.get_target_power_state()
}

fn get_nrst_voltage(&self) -> Result<f32>
{
self.0.get_nrst_voltage()
}

fn get_nrst_val(&self) -> Result<bool>
{
self.0.get_nrst_val()
}
}

impl From<Arc<Mutex<BmdRspInterface>>> for RemoteV1ADIv5
Expand Down
Loading