Skip to content

Commit 5039b76

Browse files
author
Diego Asanza
committed
Apply suggestions from code review.
Mostly comment fixes. Signed-off-by: Diego Asanza <[email protected]>
1 parent 49ddf23 commit 5039b76

File tree

6 files changed

+104
-47
lines changed

6 files changed

+104
-47
lines changed

README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ zmu supports Linux and Windows operating systems.
2929
- DWT
3030
- Cycle counter
3131
- Instruction trace
32+
- GDB Server
33+
- single stepping
34+
- range stepping
35+
- breakpoints
3236

3337
## Missing / Planned features
3438
- Time simulation / sync to real time
@@ -43,6 +47,10 @@ zmu supports Linux and Windows operating systems.
4347
- System Simulation:
4448
- device profiles, eg stm32 device support
4549
- board profiles, external peripheral simulation
50+
- GDB Server:
51+
- Reading/Writting memory
52+
- Writting Registers
53+
- Pass the port on the command line
4654

4755
## Depedencies
4856

@@ -178,3 +186,43 @@ Run the emulator:
178186
$zmu run tests/hello_world/hello_world-cm0.elf
179187
hello, world
180188
```
189+
190+
Run the GDB Server:
191+
```
192+
$zmu run --gdb tests/hello_world/hello_world-cm0.elf
193+
Starting GDB Server on port 9001 ...
194+
```
195+
196+
On a separate terminal start the gdb client:
197+
```
198+
$ gdb-multiarch tests/hello_world/hello_world-cm0.elf
199+
GNU gdb (Debian 13.1-3) 13.1
200+
Copyright (C) 2023 Free Software Foundation, Inc.
201+
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
202+
This is free software: you are free to change and redistribute it.
203+
There is NO WARRANTY, to the extent permitted by law.
204+
Type "show copying" and "show warranty" for details.
205+
This GDB was configured as "x86_64-linux-gnu".
206+
Type "show configuration" for configuration details.
207+
For bug reporting instructions, please see:
208+
<https://www.gnu.org/software/gdb/bugs/>.
209+
Find the GDB manual and other documentation resources online at:
210+
<http://www.gnu.org/software/gdb/documentation/>.
211+
212+
For help, type "help".
213+
Type "apropos word" to search for commands related to "word"...
214+
Reading symbols from tests/hello_world/hello_world-cm0.elf...
215+
(gdb) target remote localhost:9001
216+
Remote debugging using localhost:9001
217+
Reset_Handler ()
218+
at /usr/share/doc/gcc-arm-none-eabi/examples/startup/startup_ARMCM0.S:150
219+
150 ldr r1, =__etext
220+
(gdb) b main
221+
Breakpoint 1 at 0x5c: file main.c, line 6.
222+
(gdb) c
223+
Continuing.
224+
225+
Breakpoint 1, main () at main.c:6
226+
6 printf("hello, world\n");
227+
(gdb)
228+
```

zmu_cortex_m/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ byteorder = "1"
99
enum-set = "0.0.8"
1010
gdbstub = "0.7"
1111
gdbstub_arch = "0.3"
12+
log = "0.4"
1213

1314

1415
[features]

zmu_cortex_m/src/gdb/conn.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@ impl TcpConnection {
1212
let listener = TcpListener::bind(("127.0.0.1", port)).unwrap();
1313

1414
for stream in listener.incoming() {
15-
let stream = stream.unwrap();
15+
let stream = stream.map_err(|_| "Error accepting socket connection")?;
1616
stream.set_read_timeout(Some(std::time::Duration::from_millis(1)))
17-
.expect("set_read_timeout call failed");
18-
// stream.set_nonblocking(true).expect("set_nonblocking call failed");
17+
.map_err(|_| "Error setting timeout")?;
1918
return Ok(TcpConnection { stream });
2019
};
2120

@@ -79,7 +78,6 @@ impl ConnectionExt for TcpConnection {
7978
#[cfg(unix)]
8079
std::io::ErrorKind::WouldBlock => return Ok(None),
8180
_ => {
82-
println!("peek error: {:?}", e);
8381
return Err("socket peek failed")
8482
}
8583
}

zmu_cortex_m/src/gdb/server.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//!
2-
//! Flash Memory simulation
2+
//! GDB Server implementation
33
//!
44
//!
55
@@ -27,14 +27,19 @@ use crate::semihosting::SemihostingResponse;
2727
/// The gdb Server
2828
///
2929
pub struct GdbServer {
30-
// number: i32,
3130
target: ZmuTarget,
3231
}
3332

34-
///
3533
impl GdbServer {
3634

37-
///
35+
/// Create a new GDB server.
36+
///
37+
/// # Arguments
38+
///
39+
/// * `code` - The binary code to run in the emulator
40+
/// * `semihost_func` - A function that will be called when a semihosting command is issued
41+
/// * `map` - The memory map configuration
42+
/// * `flash_size` - The size of the flash memory
3843
pub fn new(
3944
code: &[u8],
4045
semihost_func: Box<dyn FnMut(&SemihostingCommand) -> SemihostingResponse + 'static>,
@@ -47,7 +52,8 @@ impl GdbServer {
4752
Ok(GdbServer {target})
4853
}
4954

50-
///
55+
/// Start the GDB Server. This function will block until the GDB client disconnects.
56+
/// or program execution is complete..
5157
pub fn start(&mut self) -> Result<u32, &'static str> {
5258
println!("Starting GDB server");
5359
let mut exit_code = 0;

zmu_cortex_m/src/gdb/simulation.rs

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,48 +14,48 @@ use crate::semihosting::SemihostingCommand;
1414
use crate::semihosting::SemihostingResponse;
1515

1616
///
17-
/// Cortex ystem simulation framework
17+
/// Cortex System simulation framework
1818
///
1919
pub struct Simulation {
2020
/// processor state
2121
pub processor: Processor,
22-
///
22+
/// Simulation Execution mode
2323
pub exec_mode: SimulationExecMode,
24-
///
24+
/// Watchpoints
2525
pub watchpoints: Vec<u32>,
26-
///
26+
/// Breakpoints
2727
pub breakpoints: Vec<u32>,
2828
}
2929

3030
///
31-
///
31+
/// A simulation event.
3232
///
3333
pub enum SimulationEvent {
34-
///
34+
/// Step done
3535
DoneStep,
36-
///
36+
/// Processor is halted
3737
#[allow(dead_code)]
3838
Halted,
39-
///
39+
/// A breakpoint was hit
4040
Break,
41-
///
41+
/// A write watchpoint was hit
4242
#[allow(dead_code)]
4343
WatchWrite(u32),
44-
///
44+
/// A read watchpoint was hit
4545
WatchRead(u32),
46-
///
46+
/// Simulation is finalized
4747
Finalized(u32)
4848
}
4949

5050
///
51-
///
51+
/// Mode of execution for the simulation
5252
///
5353
pub enum SimulationExecMode {
54-
///
54+
/// Step by Step execution (Single Step)
5555
Step,
56-
///
56+
/// Continuous execution
5757
Continue,
58-
///
58+
/// Range Step execution (from start to end)
5959
RangeStep(u32, u32),
6060
}
6161

@@ -65,18 +65,15 @@ impl Simulation {
6565
///
6666
pub fn new(code: &[u8],
6767
semihost_func: Box<dyn FnMut(&SemihostingCommand) -> SemihostingResponse + 'static>,
68-
// itm_file: Option<Box<dyn io::Write + 'static>>,
6968
map: Option<MemoryMapConfig>,
7069
flash_size: usize,
7170
) -> Result<Simulation, &'static str> {
7271
let mut processor = Processor::new();
73-
// processor.itm(itm_file);
7472
processor.semihost(Some(semihost_func));
7573
processor.memory_map(map);
7674
processor.flash_memory(flash_size, code);
77-
//processor.ram_memory(ram_size);
7875
processor.cache_instructions();
79-
processor.running = true; // running
76+
processor.running = true;
8077
match processor.reset() {
8178
Ok(_) => {},
8279
Err(_) => return Err("Error resetting processor"),
@@ -90,17 +87,27 @@ impl Simulation {
9087
})
9188
}
9289

93-
///
94-
pub fn run(&mut self, mut poll_incomming_data: impl FnMut() -> bool ) -> SimulationRunEvent {
90+
/// Run the simulation. This function will block until the simulation is complete.
91+
pub fn run(&mut self, mut poll_incoming_data: impl FnMut() -> bool ) -> SimulationRunEvent {
9592
match self.exec_mode {
9693
SimulationExecMode::Step => {
9794
return SimulationRunEvent::Event(self.step());
9895
},
9996
SimulationExecMode::Continue => {
10097
let mut cycles = 0;
10198
loop {
99+
// The poll_incoming_data function checks if there is any data available
100+
// to be read from the connection to the GDB server.
101+
// Currently, each call to poll_incoming_data blocks until either data
102+
// is available or a timeout occurs. This provides a simple mechanism
103+
// to detect if the GDB client has sent any commands to the server.
104+
// However, this approach is inefficient because the simulation is
105+
// effectively stalled until either the GDB client sends data or the
106+
// timeout expires.
107+
// To reduce the performance impact, we only poll for incoming data
108+
// every 1024 cycles.
102109
if cycles % 1024 == 0 {
103-
if poll_incomming_data() {
110+
if poll_incoming_data() {
104111
return SimulationRunEvent::IncomingData;
105112
}
106113
}
@@ -116,7 +123,7 @@ impl Simulation {
116123
let mut cycles = 0;
117124
loop {
118125
if cycles % 1024 == 0 {
119-
if poll_incomming_data() {
126+
if poll_incoming_data() {
120127
return SimulationRunEvent::IncomingData;
121128
}
122129
}
@@ -138,7 +145,7 @@ impl Simulation {
138145
}
139146

140147
///
141-
///
148+
/// Reset the simulation
142149
///
143150
pub fn reset(&mut self) -> Result<(), SimulationError> {
144151
match self.processor.reset() {
@@ -148,7 +155,7 @@ impl Simulation {
148155
}
149156

150157
///
151-
///
158+
/// Single step the simulation
152159
///
153160
pub fn step(&mut self) -> SimulationEvent {
154161
if self.processor.running {
@@ -168,12 +175,12 @@ impl Simulation {
168175
}
169176

170177
///
171-
///
178+
/// A simulation run event.
172179
///
173180
174181
pub enum SimulationRunEvent {
175-
///
182+
/// Incoming data from the GDB server
176183
IncomingData,
177-
///
184+
/// A simulation event
178185
Event(SimulationEvent),
179186
}

zmu_cortex_m/src/gdb/target.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use gdbstub::target::ext::monitor_cmd::MonitorCmd;
55
use gdbstub::common::Signal;
66
use gdbstub::outputln;
77

8+
use log::debug;
9+
810
use crate::MemoryMapConfig;
911
use crate::gdb::simulation;
1012

@@ -46,11 +48,6 @@ impl ZmuTarget {
4648
self.simulation.run(poll_incomming_data)
4749
}
4850

49-
// pub fn reset(&mut self) -> Result<(), &'static str> {
50-
// self.simulation.reset();
51-
52-
// }
53-
5451
pub fn step(&mut self) -> SimulationEvent {
5552
self.simulation.step()
5653
}
@@ -93,7 +90,7 @@ impl SingleThreadBase for ZmuTarget {
9390
&mut self,
9491
regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs,
9592
) -> TargetResult<(), Self> {
96-
print!("> read_registers");
93+
debug!("> read_registers");
9794
regs.r = self.simulation.processor.r0_12;
9895
regs.sp = self.simulation.processor.get_r(Reg::SP);
9996
regs.lr = self.simulation.processor.lr;
@@ -107,7 +104,7 @@ impl SingleThreadBase for ZmuTarget {
107104
&mut self,
108105
_regs: &gdbstub_arch::arm::reg::ArmCoreRegs
109106
) -> TargetResult<(), Self> {
110-
print!("> write_registers");
107+
debug!("> write_registers");
111108
Ok(())
112109
}
113110

@@ -117,7 +114,7 @@ impl SingleThreadBase for ZmuTarget {
117114
_start_addr: u32,
118115
data: &mut [u8],
119116
) -> TargetResult<usize, Self> {
120-
print!("> read_addrs");
117+
debug!("> read_addrs");
121118
data.iter_mut().for_each(|b| *b = 0x55);
122119
Ok(data.len())
123120
}
@@ -128,7 +125,7 @@ impl SingleThreadBase for ZmuTarget {
128125
_start_addr: u32,
129126
_data: &[u8],
130127
) -> TargetResult<(), Self> {
131-
print!("> write_addrs");
128+
debug!("> write_addrs");
132129
Ok(())
133130
}
134131

@@ -221,7 +218,7 @@ impl MonitorCmd for ZmuTarget {
221218
cmd: &[u8],
222219
mut out: gdbstub::target::ext::monitor_cmd::ConsoleOutput<'_>,
223220
) -> Result<(), Self::Error> {
224-
print!("> handle_monitor_cmd {:?}", cmd);
221+
debug!("> handle_monitor_cmd {:?}", cmd);
225222
let cmd = core::str::from_utf8(cmd).map_err(|_| "Invalid UTF-8")?;
226223
match cmd {
227224
"reset" => {

0 commit comments

Comments
 (0)