jonas-schievink / jaylink

A Rust library for talking to J-Link USB devices
BSD Zero Clause License
40 stars 12 forks source link

Old JLink firmware expects some missing initialization step(s) to work correctly. #36

Open Nable80 opened 3 years ago

Nable80 commented 3 years ago

There's a second issue that I discovered yesterday with the same old JLink-OB clone probes.

Although probe is detected correctly after connection, it fails to work and reports the lack of response from target:

> ./list
Found 1 J-Link device.

Bus 006 Address 058 Port 004: VID=1366 PID=0101 – SEGGER J-Link (Serial 000000123456)
⤷ Capabilities: GetHwVersion | ReadConfig | WriteConfig | GetMaxBlockSize | GetHwInfo | ResetStopTimed | SelectIf | GetCounters | GetCpuCaps | ExecCpuCmd | Swo | Register | GetCapsEx
  Firmware: J-Link ARM-OB STM32 compiled Aug 22 2012 19:52:04
  HW Version: J-Link 7.0.0
  Max. SWO Speed: 4500000 Hz
  Max. Memblock: 11288 bytes
  VTref: 3.3 V
  Interfaces:
      - JTAG (up to 12 MHz)
      - SWD (up to 12 MHz)

> ./swdump
speed configuration: 12000 kHz
error: no response from target chip

> ./swdump
speed configuration: 12000 kHz
error: no response from target chip

Then I start and terminate (hit Ctrl+C) the official proprietary JLink tool (I want to get rid of this piece of non-free software, that's why I'm trying to learn how to use your library), it shows that both probe and target are OK:

> JLinkGDBServer -halt -if SWD -device STM32F030F4
SEGGER J-Link GDB Server V5.02f Command Line Version

JLinkARM.dll V5.02f (DLL compiled Oct  2 2015 20:53:57)

-----GDB Server start settings-----
GDBInit file:                  none
GDB Server Listening port:     2331
SWO raw output listening port: 2332
Terminal I/O port:             2333
Accept remote connection:      yes
Generate logfile:              off
Verify download:               off
Init regs on start:            off
Silent mode:                   off
Single run mode:               off
Target connection timeout:     0 ms
------J-Link related settings------
J-Link Host interface:         USB
J-Link script:                 none
J-Link settings file:          none
------Target related settings------
Target device:                 STM32F030F4
Target interface:              SWD
Target interface speed:        1000kHz
Target endian:                 little

Connecting to J-Link...
J-Link is connected.
Firmware: J-Link ARM-OB STM32 compiled Aug 22 2012 19:52:04
Hardware: V7.00
S/N: 20090928
Feature(s): RDI,FlashDL,FlashBP,JFlash,GDBFull
Checking target voltage...
Target voltage: 3.30 V
Listening on TCP/IP port 2331
Connecting to target...Connected to target
Waiting for GDB connection...^C

And then I run swdump again and... it works too:

> ./swdump
speed configuration: 12000 kHz
DPIDR=0x0BB11477
CTRL/STAT=0xF0000040

It works until I disconnect and connect my probe again, then I have to run JLinkGDBServer again. I know this report is incomplete without traces / USB communication dumps but probably someone saw this behavior too and already know the answer.

jonas-schievink commented 3 years ago

Might be because we don't register the connection https://github.com/jonas-schievink/jaylink/pull/26

Nable80 commented 3 years ago

Thank you, this is awesome! I took that PR, changed some lines to fix compilation errors (Capabilities -> Capability, REGISTER -> Register, etc) and it seems to be working now.

My diff ```diff diff --git a/examples/swdump.rs b/examples/swdump.rs index e1031cf..697c2b1 100644 --- a/examples/swdump.rs +++ b/examples/swdump.rs @@ -20,7 +20,7 @@ //! //! This example assumes that the above "bug" (or misspecification?) is present. -use jaylink::{Interface, JayLink, SpeedConfig}; +use jaylink::{Capability, Connection, Interface, JayLink, SpeedConfig}; use log::trace; use std::{cmp, fmt}; use structopt::StructOpt; @@ -300,6 +300,10 @@ fn run(opts: Opts) -> Result<(), SwdError> { println!("speed configuration: {}", speed); probe.set_speed(speed)?; + if probe.capabilities().contains(Capability::Register) { + probe.register(Connection::default())?; + } + probe.swj_seq()?; let dpidr = probe.raw_read(Port::Debug, 0b0000)?; diff --git a/src/lib.rs b/src/lib.rs index d9e8fee..b5669d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,7 +93,7 @@ pub use self::interface::{Interface, InterfaceIter, Interfaces}; use self::bits::IteratorExt as _; use self::error::ResultExt as _; use bitflags::bitflags; -use byteorder::{LittleEndian, ReadBytesExt}; +use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; use io::Cursor; use log::{debug, trace, warn}; use std::cell::{Cell, RefCell, RefMut}; @@ -162,6 +162,15 @@ enum Command { ReadConfig = 0xF2, WriteConfig = 0xF3, + + Register = 0x09, +} + +#[repr(u8)] +#[allow(dead_code)] +enum RegisterCommand { + Register = 0x64, + Unregister = 0x65, } #[repr(u8)] @@ -208,6 +217,15 @@ impl SwoStatus { } } +#[derive(Default, Debug)] +pub struct Connection { + handle: u16, + pid: u32, + hid: Option, + iid: u8, + cid: u8, +} + /// A handle to a J-Link USB device. /// /// This is the main interface type of this library. There are multiple ways of obtaining an @@ -628,7 +646,7 @@ impl JayLink { /// /// This requires the probe to support [`Capability::SpeedInfo`]. pub fn read_speeds(&self) -> Result { - self.require_capability(Capability::SpeedInfo)?; + // self.require_capability(Capability::SpeedInfo)?; self.write_cmd(&[Command::GetSpeeds as u8])?; @@ -882,6 +900,30 @@ impl JayLink { Ok(()) } + /// Registers a connection on the device. + /// + /// This requires the probe to support [`Capability::Register`]. + /// + /// **Note**: This may be **required** on some devices for SWD to work at all. + /// + /// [`REGISTER`]: Capabilities::REGISTER + pub fn register(&self, conn: Connection) -> Result<()> { + self.require_capability(Capability::Register)?; + let mut buf = Vec::with_capacity(14); + buf.push(Command::Register as u8); + buf.push(RegisterCommand::Register as u8); + buf.write_u32::(conn.pid).unwrap(); + buf.write_u32::(conn.hid.unwrap_or(std::net::Ipv4Addr::LOCALHOST).into()) + .unwrap(); + buf.push(conn.iid); + buf.push(conn.cid); + buf.write_u16::(conn.handle).unwrap(); + self.write_cmd(&buf)?; + let mut buf = [0; 76]; + self.read(&mut buf)?; + Ok(()) + } + /// Performs a JTAG I/O operation. /// /// This will shift out data on `TMS` (pin 7) and `TDI` (pin 5), while reading data shifted ```

Should I close this issue now or it's better to wait until #26 is prettified and merged?

valpackett commented 3 years ago

Not hard failing require_capability(Capability::SpeedInfo) (as you've done in this patch) should be also done somewhere I guess… ah this is tracked as #35