I2C works

This commit is contained in:
Sebastian 2019-08-23 18:50:19 +02:00
parent 9f55b6dc08
commit 03195921b8
4 changed files with 299 additions and 131 deletions

388
.gdbdash
View File

@ -5,11 +5,9 @@ python
# https://github.com/cyrus-and/gdb-dashboard
import ast
import fcntl
import os
import re
import struct
import termios
import traceback
import math
@ -34,8 +32,23 @@ The list of all the available styles can be obtained with (from GDB itself):
python from pygments.styles import get_all_styles as styles
python for s in styles(): print(s)
""",
'default': 'vim',
'type': str
'default': 'vim'
},
# values formatting
'compact_values': {
'doc': 'Display complex objects in a single line.',
'default': False,
'type': bool
},
'max_value_length': {
'doc': 'Maximum length for displayed values.',
'default': 0,
'type': int
},
'dereference': {
'doc': 'Annotate pointers with the pointed value.',
'default': True,
'type': bool
},
# prompt
'prompt': {
@ -183,39 +196,59 @@ def to_string(value):
value_string = str(value)
except UnicodeEncodeError:
value_string = unicode(value).encode('utf8')
except gdb.error as e:
value_string = ansi(e, R.style_error)
return value_string
def format_address(address):
pointer_size = gdb.parse_and_eval('$pc').type.sizeof
return ('0x{{:0{}x}}').format(pointer_size * 2).format(address)
def format_value(value):
def format_value(value, compact=None):
# format references as referenced values
# (TYPE_CODE_RVALUE_REF is not supported by old GDB)
if value.type.code in (getattr(gdb, 'TYPE_CODE_REF', None),
getattr(gdb, 'TYPE_CODE_RVALUE_REF', None)):
try:
return to_string(value.referenced_value())
except gdb.MemoryError:
return to_string(value)
else:
try:
return to_string(value)
except gdb.MemoryError as e:
return ansi(e, R.style_error)
value = value.referenced_value()
# format the value
out = to_string(value)
# dereference up to the actual value if requested
if R.dereference and value.type.code == gdb.TYPE_CODE_PTR:
while value.type.code == gdb.TYPE_CODE_PTR:
try:
value = value.dereference()
except gdb.error as e:
break
else:
formatted = to_string(value)
out += '{} {}'.format(ansi(':', R.style_low), formatted)
# compact the value
if compact is not None and compact or R.compact_values:
out = re.sub(r'$\s*', '', out, flags=re.MULTILINE)
# truncate the value
if R.max_value_length > 0 and len(out) > R.max_value_length:
out = out[0:R.max_value_length] + ansi('[...]', R.style_error)
return out
class Beautifier():
def __init__(self, filename, tab_size=4):
def __init__(self, hint, tab_size=4):
self.tab_spaces = ' ' * tab_size
self.active = False
if not R.ansi:
return
# attempt to set up Pygments
try:
from pygments.lexers import get_lexer_for_filename
import pygments
from pygments.lexers import GasLexer, NasmLexer
from pygments.formatters import Terminal256Formatter
if hint == 'att':
self.lexer = GasLexer()
elif hint == 'intel':
self.lexer = NasmLexer()
else:
from pygments.lexers import get_lexer_for_filename
self.lexer = get_lexer_for_filename(hint, stripnl=False)
self.formatter = Terminal256Formatter(style=R.syntax_highlighting)
self.lexer = get_lexer_for_filename(filename, stripnl=False)
self.active = True
except ImportError:
# Pygments not available
@ -383,8 +416,13 @@ class Dashboard(gdb.Command):
# skip disabled modules
if not instance:
continue
# ask the module to generate the content
lines = instance.lines(width, style_changed)
try:
# ask the module to generate the content
lines = instance.lines(width, style_changed)
except Exception as e:
# allow to continue on exceptions in modules
stacktrace = traceback.format_exc().strip()
lines = [ansi(stacktrace, R.style_error)]
# create the divider accordingly
div = divider(width, instance.label(), True, lines)
# write the data
@ -427,10 +465,22 @@ class Dashboard(gdb.Command):
@staticmethod
def get_term_width(fd=1): # defaults to the main terminal
# first 2 shorts (4 byte) of struct winsize
raw = fcntl.ioctl(fd, termios.TIOCGWINSZ, ' ' * 4)
height, width = struct.unpack('hh', raw)
return int(width)
if sys.platform == 'win32':
try:
import curses
# XXX always neglects the fd parameter
_, width = curses.initscr().getmaxyx()
curses.endwin()
return int(width)
except ImportError:
return 80 # hardcoded fallback value
else:
import termios
import fcntl
# first 2 shorts (4 byte) of struct winsize
raw = fcntl.ioctl(fd, termios.TIOCGWINSZ, ' ' * 4)
_, width = struct.unpack('hh', raw)
return int(width)
@staticmethod
def set_custom_prompt(dashboard):
@ -959,11 +1009,9 @@ instructions constituting the current statement are marked, if available."""
frame = gdb.selected_frame() # PC is here
disassemble = frame.architecture().disassemble
try:
# try to fetch the function boundaries using the disassemble command
output = run('disassemble').split('\n')
start = int(re.split('[ :]', output[1][3:], 1)[0], 16)
end = int(re.split('[ :]', output[-3][3:], 1)[0], 16)
asm = disassemble(start, end_pc=end)
# disassemble the current block
block = gdb.block_for_pc(frame.pc())
asm = disassemble(block.start, end_pc=block.end - 1)
# find the location of the PC
pc_index = next(index for index, instr in enumerate(asm)
if instr['addr'] == frame.pc())
@ -974,7 +1022,7 @@ instructions constituting the current statement are marked, if available."""
# line_info is not None but line_info.last is None
line_info = gdb.find_pc_line(frame.pc())
line_info = line_info if line_info.last else None
except (gdb.error, StopIteration):
except (gdb.error, RuntimeError, StopIteration):
# if it is not possible (stripped binary or the PC is not present in
# the output of `disassemble` as per issue #31) start from PC and
# end after twice the context
@ -995,17 +1043,13 @@ instructions constituting the current statement are marked, if available."""
func_start = to_unsigned(value)
except gdb.error:
pass # e.g., @plt
# fetch the assembly flavor and the extension used by Pygments
# fetch the assembly flavor anduse it as hint for Pygments
try:
flavor = gdb.parameter('disassembly-flavor')
except:
flavor = None # not always defined (see #36)
filename = {
'att': '.s',
'intel': '.asm'
}.get(flavor, '.s')
flavor = 'att' # not always defined (see #36)
# prepare the highlighter
highlighter = Beautifier(filename)
highlighter = Beautifier(flavor)
# compute the maximum offset size
if func_start:
max_offset = max(len(str(abs(asm[0]['addr'] - func_start))),
@ -1087,6 +1131,84 @@ instructions constituting the current statement are marked, if available."""
}
}
class Variables(Dashboard.Module):
"""Show arguments and locals of the selected frame."""
def label(self):
return 'Variables'
def lines(self, term_width, style_changed):
return Variables.format_variables(
gdb.selected_frame(),
self.show_arguments, self.show_locals, self.compact)
def attributes(self):
return {
'arguments': {
'doc': 'Frame arguments visibility flag.',
'default': True,
'name': 'show_arguments',
'type': bool
},
'locals': {
'doc': 'Frame locals visibility flag.',
'default': True,
'name': 'show_locals',
'type': bool
},
'compact': {
'doc': 'Single-line display flag.',
'default': False,
'type': bool
}
}
@staticmethod
def format_variables(frame, show_arguments, show_locals, compact):
out = []
# fetch frame arguments and locals
decorator = gdb.FrameDecorator.FrameDecorator(frame)
separator = ansi(', ', R.style_low)
if show_arguments:
def prefix(line):
return Stack.format_line('arg', line)
frame_args = decorator.frame_args()
args_lines = Variables.fetch(frame, frame_args, compact)
if args_lines:
if compact:
args_line = separator.join(args_lines)
single_line = prefix(args_line)
out.append(single_line)
else:
out.extend(map(prefix, args_lines))
else:
out.append(ansi('(no arguments)', R.style_low))
if show_locals:
def prefix(line):
return Stack.format_line('loc', line)
frame_locals = decorator.frame_locals()
locals_lines = Variables.fetch(frame, frame_locals, compact)
if locals_lines:
if compact:
locals_line = separator.join(locals_lines)
single_line = prefix(locals_line)
out.append(single_line)
else:
out.extend(map(prefix, locals_lines))
else:
out.append(ansi('(no locals)', R.style_low))
return out
@staticmethod
def fetch(frame, data, compact):
lines = []
for elem in data or []:
name = elem.sym
equal = ansi('=', R.style_low)
value = format_value(elem.sym.value(frame), compact)
lines.append('{} {} {}'.format(name, equal, value))
return lines
class Stack(Dashboard.Module):
"""Show the current stack trace including the function name and the file
location, if available. Optionally list the frame arguments and locals too."""
@ -1119,40 +1241,9 @@ location, if available. Optionally list the frame arguments and locals too."""
info = Stack.get_pc_line(frame, style)
frame_lines = []
frame_lines.append('[{}] {}'.format(frame_id, info))
# fetch frame arguments and locals
decorator = gdb.FrameDecorator.FrameDecorator(frame)
separator = ansi(', ', R.style_low)
strip_newlines = re.compile(r'$\s*', re.MULTILINE)
if self.show_arguments:
def prefix(line):
return Stack.format_line('arg', line)
frame_args = decorator.frame_args()
args_lines = Stack.fetch_frame_info(frame, frame_args)
if args_lines:
if self.compact:
args_line = separator.join(args_lines)
args_line = strip_newlines.sub('', args_line)
single_line = prefix(args_line)
frame_lines.append(single_line)
else:
frame_lines.extend(map(prefix, args_lines))
else:
frame_lines.append(ansi('(no arguments)', R.style_low))
if self.show_locals:
def prefix(line):
return Stack.format_line('loc', line)
frame_locals = decorator.frame_locals()
locals_lines = Stack.fetch_frame_info(frame, frame_locals)
if locals_lines:
if self.compact:
locals_line = separator.join(locals_lines)
locals_line = strip_newlines.sub('', locals_line)
single_line = prefix(locals_line)
frame_lines.append(single_line)
else:
frame_lines.extend(map(prefix, locals_lines))
else:
frame_lines.append(ansi('(no locals)', R.style_low))
# add frame arguments and locals
frame_lines.extend(Variables.format_variables(
frame, self.show_arguments, self.show_locals, self.compact))
# add frame
frames.append(frame_lines)
# next
@ -1178,16 +1269,6 @@ location, if available. Optionally list the frame arguments and locals too."""
prefix = ansi(prefix, R.style_low)
return '{} {}'.format(prefix, line)
@staticmethod
def fetch_frame_info(frame, data):
lines = []
for elem in data or []:
name = elem.sym
equal = ansi('=', R.style_low)
value = format_value(elem.sym.value(frame))
lines.append('{} {} {}'.format(name, equal, value))
return lines
@staticmethod
def get_pc_line(frame, style):
frame_pc = ansi(format_address(frame.pc()), style)
@ -1220,13 +1301,13 @@ location, if available. Optionally list the frame arguments and locals too."""
return {
'limit': {
'doc': 'Maximum number of displayed frames (0 means no limit).',
'default': 2,
'default': 10,
'type': int,
'check': check_ge_zero
},
'arguments': {
'doc': 'Frame arguments visibility flag.',
'default': True,
'default': False,
'name': 'show_arguments',
'type': bool
},
@ -1275,6 +1356,61 @@ class History(Dashboard.Module):
class Memory(Dashboard.Module):
"""Allow to inspect memory regions."""
class Region():
def __init__(self, address, length, module):
self.address = address
self.length = length
self.module = module
self.original = None
self.latest = None
def format(self):
# fetch the memory content
try:
inferior = gdb.selected_inferior()
memory = inferior.read_memory(self.address, self.length)
# set the original memory snapshot if needed
if not self.original:
self.original = memory
except gdb.error:
msg = 'Cannot access {} bytes starting at {}'
msg = msg.format(self.length, format_address(self.address))
return [ansi(msg, R.style_error)]
# format the memory content
out = []
for i in range(0, len(memory), self.module.row_length):
region = memory[i:i + self.module.row_length]
pad = self.module.row_length - len(region)
address = format_address(self.address + i)
# compute changes
hexa = []
text = []
for j in range(len(region)):
rel = i + j
byte = memory[rel]
hexa_byte = '{:02x}'.format(ord(byte))
text_byte = Memory.format_byte(byte)
# differences against the latest have the highest priority
if self.latest and memory[rel] != self.latest[rel]:
hexa_byte = ansi(hexa_byte, R.style_selected_1)
text_byte = ansi(text_byte, R.style_selected_1)
# cumulative changes if enabled
elif (self.module.cumulative and
memory[rel] != self.original[rel]):
hexa_byte = ansi(hexa_byte, R.style_selected_2)
text_byte = ansi(text_byte, R.style_selected_2)
hexa.append(hexa_byte)
text.append(text_byte)
# output the formatted line
out.append('{} {}{} {}{}'.format(
ansi(address, R.style_low),
' '.join(hexa), ansi(pad * ' --', R.style_low),
''.join(text), ansi(pad * '.', R.style_low)))
# update the latest memory snapshot
self.latest = memory
return out
@staticmethod
def format_byte(byte):
# `type(byte) is bytes` in Python 3
@ -1294,35 +1430,13 @@ class Memory(Dashboard.Module):
self.row_length = 16
self.table = {}
def format_memory(self, start, memory):
out = []
for i in range(0, len(memory), self.row_length):
region = memory[i:i + self.row_length]
pad = self.row_length - len(region)
address = format_address(start + i)
hexa = (' '.join('{:02x}'.format(ord(byte)) for byte in region))
text = (''.join(Memory.format_byte(byte) for byte in region))
out.append('{} {}{} {}{}'.format(ansi(address, R.style_low),
hexa,
ansi(pad * ' --', R.style_low),
ansi(text, R.style_high),
ansi(pad * '.', R.style_low)))
return out
def label(self):
return 'Memory'
def lines(self, term_width, style_changed):
out = []
inferior = gdb.selected_inferior()
for address, length in sorted(self.table.items()):
try:
memory = inferior.read_memory(address, length)
out.extend(self.format_memory(address, memory))
except gdb.error:
msg = 'Cannot access {} bytes starting at {}'
msg = msg.format(length, format_address(address))
out.append(ansi(msg, R.style_error))
for address, region in sorted(self.table.items()):
out.extend(region.format())
out.append(divider(term_width))
# drop last divider
if out:
@ -1337,7 +1451,7 @@ class Memory(Dashboard.Module):
length = Memory.parse_as_address(length)
else:
length = self.row_length
self.table[address] = length
self.table[address] = Memory.Region(address, length, self)
else:
raise Exception('Specify an address')
@ -1372,6 +1486,15 @@ class Memory(Dashboard.Module):
}
}
def attributes(self):
return {
'cumulative': {
'doc': 'Highlight changes cumulatively, watch again to reset.',
'default': False,
'type': bool
}
}
class Registers(Dashboard.Module):
"""Show the CPU registers and their values."""
@ -1385,11 +1508,18 @@ class Registers(Dashboard.Module):
# skip if the current thread is not stopped
if not gdb.selected_thread().is_stopped():
return []
# obtain the registers to display
if style_changed:
self.table = {}
if self.register_list:
register_list = self.register_list.split()
else:
register_list = list(map(lambda line: line.split(None, 1)[0],
run('info registers').strip().split('\n')))
# fetch registers status
registers = []
for reg_info in run('info registers').strip().split('\n'):
# fetch register and update the table
name = reg_info.split(None, 1)[0]
for name in register_list:
# Exclude registers with a dot '.' or parse_and_eval() will fail
if '.' in name:
continue
@ -1404,15 +1534,10 @@ class Registers(Dashboard.Module):
max_name = max(len(name) for name, _, _ in registers)
max_value = max(len(value) for _, value, _ in registers)
max_width = max_name + max_value + 2
per_line = int((term_width + 1) / max_width) or 1
per_line = min(int((term_width + 1) / max_width) or 1, len(registers))
# redistribute extra space among columns
extra = int((term_width + 1 - max_width * per_line) / per_line)
if per_line == 1:
# center when there is only one column
max_name += int(extra / 2)
max_value += int(extra / 2)
else:
max_value += extra
max_name += int(extra / 2)
# format registers info
partial = []
for name, value, changed in registers:
@ -1440,6 +1565,12 @@ class Registers(Dashboard.Module):
'default': False,
'name': 'column_major',
'type': bool
},
'list': {
'doc': """String of space-separated register names to display.
The empty list (default) causes to show all the available registers.""",
'default': '',
'name': 'register_list',
}
}
@ -1468,13 +1599,22 @@ class Threads(Dashboard.Module):
restore_frame = gdb.selected_thread().is_stopped()
if restore_frame:
selected_frame = gdb.selected_frame()
for thread in gdb.Inferior.threads(gdb.selected_inferior()):
# fetch the thread list
threads = []
for inferior in gdb.inferiors():
if self.all_inferiors or inferior == gdb.selected_inferior():
threads += gdb.Inferior.threads(inferior)
for thread in threads:
# skip running threads if requested
if self.skip_running and thread.is_running():
continue
is_selected = (thread.ptid == selected_thread.ptid)
style = R.style_selected_1 if is_selected else R.style_selected_2
number = ansi(str(thread.num), style)
if self.all_inferiors:
number = '{}.{}'.format(thread.inferior.num, thread.num)
else:
number = str(thread.num)
number = ansi(number, style)
tid = ansi(str(thread.ptid[1] or thread.ptid[2]), style)
info = '[{}] id {}'.format(number, tid)
if thread.name:
@ -1500,7 +1640,13 @@ class Threads(Dashboard.Module):
'default': False,
'name': 'skip_running',
'type': bool
}
},
'all-inferiors': {
'doc': 'Show threads from all inferiors.',
'default': False,
'name': 'all_inferiors',
'type': bool
},
}
class Expressions(Dashboard.Module):
@ -1579,7 +1725,7 @@ set python print-stack full
python Dashboard.start()
# ------------------------------------------------------------------------------
# Copyright (c) 2015-2017 Andrea Cardaci <cyrus.and@gmail.com>
# Copyright (c) 2015-2019 Andrea Cardaci <cyrus.and@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal

View File

@ -2,7 +2,7 @@
authors = ["sebastian"]
edition = "2018"
readme = "README.md"
name = "STM32F1Test"
name = "cheapsdo"
version = "0.1.0"
[dependencies]
@ -27,7 +27,7 @@ features = ["unproven"]
# this lets you use `cargo fix`!
[[bin]]
name = "STM32F1Test"
name = "cheapsdo"
test = false
bench = false

2
run.sh
View File

@ -1,3 +1,3 @@
#!/bin/bash
cargo build || exit -1
arm-none-eabi-gdb target/thumbv7m-none-eabi/debug/STM32F1Test
arm-none-eabi-gdb target/thumbv7m-none-eabi/debug/cheapsdo

View File

@ -1,21 +1,16 @@
//! Blinks an LED
//!
//! This assumes that a LED is connected to pc13 as is the case on the blue pill board.
//!
//! Note: Without additional hardware, PC13 should not be used to drive an LED, see page 5.1.2 of
//! the reference manaual for an explanation. This is not an issue on the blue pill.
#![deny(unsafe_code)]
#![no_std]
#![no_main]
use panic_halt as _;
use cortex_m_semihosting::hprintln; // logs messages to the host; requires a debugger
use nb::block;
use stm32f1xx_hal::{
prelude::*,
pac,
i2c,
timer::Timer,
};
use cortex_m_rt::entry;
@ -23,6 +18,7 @@ use embedded_hal::digital::v2::OutputPin;
#[entry]
fn main() -> ! {
// Get access to the core peripherals from the cortex-m crate
let cp = cortex_m::Peripherals::take().unwrap();
// Get access to the device specific peripherals from the peripheral access crate
@ -32,6 +28,7 @@ fn main() -> ! {
// HAL structs
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
// Freeze the configuration of all the clocks in the system and store the frozen frequencies in
// `clocks`
@ -40,6 +37,23 @@ fn main() -> ! {
// Acquire the GPIOC peripheral
let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
let scl = gpiob.pb6.into_alternate_open_drain(&mut gpiob.crl);
let sda = gpiob.pb7.into_alternate_open_drain(&mut gpiob.crl);
let mut i2c = i2c::BlockingI2c::i2c1(dp.I2C1, (scl, sda),
&mut afio.mapr,
i2c::Mode::Standard{frequency: 400_000},
clocks,
&mut rcc.apb1,
5,
1,
5,
5);
hprintln!("I2C setup").unwrap();
// Configure gpio C pin 13 as a push-pull output. The `crh` register is passed to the function
// in order to configure the port. For pins 0-7, crl should be passed instead.
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
@ -50,6 +64,14 @@ fn main() -> ! {
loop {
block!(timer.wait()).unwrap();
led.set_high().unwrap();
let res = i2c.write(96, &[0x23, 0x82]);
if res.is_ok() {
hprintln!("write worked!").unwrap();
} else {
hprintln!("write failed!").unwrap();
}
block!(timer.wait()).unwrap();
led.set_low().unwrap();
}