msp430_bsl 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/dump_flash +171 -0
- data/bin/upload_hex +72 -16
- data/lib/core_ext/number.rb +8 -1
- data/lib/msp430_bsl/command.rb +2 -5
- data/lib/msp430_bsl/configs.rb +8 -0
- data/lib/msp430_bsl/data_file.rb +32 -0
- data/lib/msp430_bsl/hex_file.rb +33 -13
- data/lib/msp430_bsl/hex_line.rb +15 -1
- data/lib/msp430_bsl/raw_data_file.rb +12 -0
- data/lib/msp430_bsl/response.rb +5 -1
- data/lib/msp430_bsl/uart/connection.rb +9 -14
- data/lib/msp430_bsl/uart/exceptions.rb +12 -6
- data/lib/msp430_bsl/uart/peripheral_interface.rb +4 -4
- data/lib/msp430_bsl/version.rb +1 -1
- metadata +10 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fdfa8cabae9d6f5217854970766871bafec9efb729f1bba0d312e55065d3dfc2
|
|
4
|
+
data.tar.gz: 25dfc816f5e59d87b5df402c395248a0c09003e54be108608ec65bbf26715c11
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 52ad0b1653f522000fafc6553e100beeff336e560ca9608fd91d804cfd124c334ebe60b4a70aebb19d358373b9d31b6efb11a5629f9659db43c04334d5d268a8
|
|
7
|
+
data.tar.gz: 671065d4376b40a99f9716e091cac284375f3bac2402b4300d16e6c19d741307a954b5dc6161728f0f682c1028eb3ba2100ef7ba218ca314acbd6dc416e17c07
|
data/bin/dump_flash
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'slop'
|
|
4
|
+
require_relative '../lib/msp430_bsl'
|
|
5
|
+
|
|
6
|
+
# Force sync on STDOUT write. This way the logger flushes its output after every write
|
|
7
|
+
STDOUT.sync = true
|
|
8
|
+
|
|
9
|
+
include Msp430Bsl::Utils
|
|
10
|
+
|
|
11
|
+
MAX_WRITE_ATTEMPTS = 3
|
|
12
|
+
SUPPORTED_OUTPUT_FORMATS = %w(hex raw_data)
|
|
13
|
+
EXTRACT_PASS_FROM_FILE_REGEX = /\:10FFE000(.{32})|\:10FFF000(.{32})/.freeze
|
|
14
|
+
|
|
15
|
+
@opts = {}
|
|
16
|
+
begin
|
|
17
|
+
@opts = Slop.parse help: true do |o|
|
|
18
|
+
o.string '-d', '--device', 'Mandatory: Path to serial programmer device', required: true
|
|
19
|
+
o.string '-o', '--outfile', 'Path to file where to save flash content. Default STDOUT', required: false
|
|
20
|
+
o.string '-g', '--logfile', 'Path to logfile'
|
|
21
|
+
o.string '-p', '--password', '32 bytes password string needed to unlock BSL. Defaults to 32 times 0xFF', default: Msp430Bsl::Configs::CMD_RX_PASSWORD
|
|
22
|
+
o.string '-t', '--extract_pass_from', 'Hex file path from where to extract the BSL password. Looks for the 32 words from 0xFFE0 to 0xFFFF'
|
|
23
|
+
o.array '-r', '--memrange', 'Memory range to read'
|
|
24
|
+
o.string '-s', '--memstartaddr', "Memory's starting address to read from. Defaults to 0x8000", default: 0x8000
|
|
25
|
+
o.string '-e', '--memendaddr', "Memory's last address to read. Defaults to 0xFFFF", default: 0x10000
|
|
26
|
+
o.string '-l', '--loglevel', "Logger level. One of ['fatal', 'error', 'warn', 'info', 'debug']. Default: 'info'", default: :info
|
|
27
|
+
o.string '-f', '--out_format', "Format of output. Supported formats: [hex, raw_data]. Default 'hex' (Intel hex)", default: 'hex'
|
|
28
|
+
o.integer '-b', '--baud', 'BAUD rate with which communicate to BSL. Default: 115200', default: 115200
|
|
29
|
+
o.bool '-h', '--help', 'Print this help' do
|
|
30
|
+
puts "#{o}\n"
|
|
31
|
+
exit
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
rescue Slop::MissingArgument => e
|
|
35
|
+
puts "Error: #{e}. Maybe you specified an empty argument?"
|
|
36
|
+
exit
|
|
37
|
+
rescue Slop::UnknownOption => e
|
|
38
|
+
puts "Error: #{e}"
|
|
39
|
+
exit
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def logger
|
|
43
|
+
@logger ||= build_logger_from @opts
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def outfile
|
|
47
|
+
@outfile ||= if @opts[:outfile]
|
|
48
|
+
File.open(File.expand_path(@opts[:outfile]), 'w')
|
|
49
|
+
else
|
|
50
|
+
STDOUT
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Validations
|
|
55
|
+
unless SUPPORTED_OUTPUT_FORMATS.include? @opts[:out_format]
|
|
56
|
+
logger.error "Output file format '#{@opts[:out_format]} is not supported. Supported formats: #{SUPPORTED_OUTPUT_FORMATS.join ','}'"
|
|
57
|
+
exit 1
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
unless Msp430Bsl::Configs::BAUD_RATES.include? @opts[:baud]
|
|
61
|
+
logger.error "BAUD rate #{@opts[:baud]} not supported. Available BAUD rates: #{ Msp430Bsl::Configs::BAUD_RATES.join ', ' }"
|
|
62
|
+
exit 2
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if @opts[:memrange].any? && @opts[:memrange].size.odd?
|
|
66
|
+
logger.error "Memory ranges must be provided in pairs. Provided 'memrange' argument with #{@opts[:memrange].size} values => #{@opts[:memrange]}"
|
|
67
|
+
exit 3
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# Conversions, normalizations, etc...
|
|
72
|
+
@opts[:memstartaddr] = @opts[:memstartaddr].to_i(16) rescue @opts[:memstartaddr]
|
|
73
|
+
@opts[:memendaddr] = @opts[:memendaddr].to_i(16) rescue @opts[:memendaddr]
|
|
74
|
+
|
|
75
|
+
# If memrange has been provided, convert values to integers parsing as hex
|
|
76
|
+
# else set memrange as [memstartaddr, memendaddr]
|
|
77
|
+
if @opts[:memrange].any?
|
|
78
|
+
@opts[:memrange].map! { |el| el.to_i(16) }
|
|
79
|
+
else
|
|
80
|
+
@opts[:memrange] = [ @opts[:memstartaddr], @opts[:memendaddr] ]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# If password param has been provided, convert given string
|
|
84
|
+
if @opts[:password] && !@opts[:password].eql?(Msp430Bsl::Configs::CMD_RX_PASSWORD)
|
|
85
|
+
@opts[:password] = @opts[:password].scan(EXTRACT_PASS_FROM_FILE_REGEX).flatten.compact.join.to_hex_ary
|
|
86
|
+
unless @opts[:password].any?
|
|
87
|
+
logger.error "Wrong password provided. Please copy entirely hex file rows staring with :10FFE0 and :10FFF0"
|
|
88
|
+
exit
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
# If extract_pass_from param has been provided, extract BSL password from given file path
|
|
92
|
+
if @opts[:extract_pass_from]
|
|
93
|
+
logger.info "Extracting BSL password from #{@opts[:extract_pass_from]}"
|
|
94
|
+
file_content = File.read @opts[:extract_pass_from]
|
|
95
|
+
lines = file_content.scan(EXTRACT_PASS_FROM_FILE_REGEX).flatten.compact
|
|
96
|
+
@opts[:password] = lines.map { |line| line.scan(/.{2}/).map { |el| el.to_i 16 }}.flatten
|
|
97
|
+
logger.debug "BSL pass: #{@opts[:password].to_hex.join}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Handy methods
|
|
101
|
+
|
|
102
|
+
# def can_we_ask_another_line?(already_asked_count:, from_addr: , to_addr:, line_size:)
|
|
103
|
+
# # We can't exceed Msp430Bsl::Configs::PURE_DATA_MAX_SIZE
|
|
104
|
+
# return false if (Msp430Bsl::Configs::PURE_DATA_MAX_SIZE - (already_asked_count * line_size)) < line_size
|
|
105
|
+
#
|
|
106
|
+
# # We can't ask lines beyond memendaddr memory address
|
|
107
|
+
# return false if (from_addr + ((already_asked_count + 1) * line_size)) >= to_addr
|
|
108
|
+
#
|
|
109
|
+
# true
|
|
110
|
+
# end
|
|
111
|
+
|
|
112
|
+
def num_of_bytes_to_fetch(from_addr, to_addr)
|
|
113
|
+
[Msp430Bsl::Configs::PURE_DATA_MAX_SIZE, (to_addr - from_addr)].min
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
### Core
|
|
118
|
+
|
|
119
|
+
# Build UART Connection
|
|
120
|
+
@board = Msp430Bsl::Uart::Connection.new @opts[:device], logger: logger
|
|
121
|
+
|
|
122
|
+
# Enter BSL
|
|
123
|
+
@board.enter_bsl
|
|
124
|
+
logger.info "Unlocking BSL's password protected commands"
|
|
125
|
+
@board.send_command :rx_password, data: @opts[:password]
|
|
126
|
+
@board.send_command :lock_unlock_info
|
|
127
|
+
# Switch UART to max speed
|
|
128
|
+
logger.info "Changing UART BAUD to #{@opts[:baud]}"
|
|
129
|
+
@board.send_command :change_baud_rate, data: Msp430Bsl::Configs::BAUD_RATES[@opts[:baud]]
|
|
130
|
+
@board.set_uart_speed @opts[:baud]
|
|
131
|
+
|
|
132
|
+
data_file = case @opts[:out_format]
|
|
133
|
+
when 'hex', :hex
|
|
134
|
+
Msp430Bsl::HexFile.new
|
|
135
|
+
when 'raw_data', :raw_data
|
|
136
|
+
Msp430Bsl::RawDataFile.new
|
|
137
|
+
else
|
|
138
|
+
Msp430Bsl::HexFile.new
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
logger.info "Reading flash content..."
|
|
142
|
+
|
|
143
|
+
@opts[:memrange].each_slice(2) do |range|
|
|
144
|
+
current_addr = range[0]
|
|
145
|
+
endaddr = range[1]
|
|
146
|
+
fetched_data = []
|
|
147
|
+
while current_addr < endaddr
|
|
148
|
+
num_bytes = num_of_bytes_to_fetch current_addr, endaddr
|
|
149
|
+
|
|
150
|
+
logger.debug "fetching #{num_bytes} bytes from addr 0x#{current_addr.to_hex_str} to addr 0x#{(current_addr + num_bytes).to_hex_str}"
|
|
151
|
+
resp = @board.send_command :tx_data_block, addr: current_addr, data: num_bytes.to_bytes_ary
|
|
152
|
+
fetched_data << resp.data
|
|
153
|
+
current_addr += num_bytes
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
data_file.add_new_lines_from(fetched_data.flatten, range[0])
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
outfile.write data_file
|
|
160
|
+
outfile.write "\n\n"
|
|
161
|
+
|
|
162
|
+
# Reset board if requested to
|
|
163
|
+
if @opts[:reset]
|
|
164
|
+
logger.info "Resetting board"
|
|
165
|
+
@board.trigger_reset
|
|
166
|
+
end
|
|
167
|
+
logger.info "Closing connection"
|
|
168
|
+
@board.close_connection
|
|
169
|
+
if outfile != STDOUT
|
|
170
|
+
logger.info "Flash content dumped to #{outfile.path}"
|
|
171
|
+
end
|
data/bin/upload_hex
CHANGED
|
@@ -9,6 +9,7 @@ STDOUT.sync = true
|
|
|
9
9
|
include Msp430Bsl::Utils
|
|
10
10
|
|
|
11
11
|
MAX_WRITE_ATTEMPTS = 3
|
|
12
|
+
EXTRACT_PASS_FROM_FILE_REGEX = /\:10FFE000(.{32})|\:10FFF000(.{32})/.freeze
|
|
12
13
|
|
|
13
14
|
@opts = {}
|
|
14
15
|
begin
|
|
@@ -16,9 +17,13 @@ begin
|
|
|
16
17
|
o.string '-d', '--device', 'Mandatory: Path to serial programmer device', required: true
|
|
17
18
|
o.string '-f', '--hexfile', 'Mandatory: Path to HEX file to load', required: true
|
|
18
19
|
o.string '-g', '--logfile', 'Path to logfile'
|
|
19
|
-
o.string '-l', '--loglevel', "Logger level. One of ['fatal', 'error', 'warn', 'info', 'debug']. Default: '
|
|
20
|
+
o.string '-l', '--loglevel', "Logger level. One of ['fatal', 'error', 'warn', 'info', 'debug']. Default: 'info'", default: :info
|
|
20
21
|
o.integer '-b', '--baud', 'BAUD rate with which communicate to BSL. Default: 115200', default: 115200
|
|
22
|
+
o.string '-p', '--password', '32 bytes password string needed to unlock BSL. Defaults to 32 times 0xFF', default: Msp430Bsl::Configs::CMD_RX_PASSWORD
|
|
23
|
+
o.string '-t', '--extract_pass_from', 'Hex file path from where to extract the BSL password. Looks for the 32 words from 0xFFE0 to 0xFFFF'
|
|
24
|
+
o.bool '-r', '--reset', 'Reset board after a successful upload', default: false
|
|
21
25
|
o.bool '-e', '--erase_info', 'Erase info memory. Default: false', default: false # TODO: implement
|
|
26
|
+
o.bool '-n', '--no_mass_erase', 'Skip mass erase. Default: false (By default flash gets totally erased)', default: false
|
|
22
27
|
o.bool '-c', '--check', 'Verify flash content after each data block write. Default: true', default: true
|
|
23
28
|
o.bool '-h', '--help', 'Print this help' do
|
|
24
29
|
puts "#{o}\n"
|
|
@@ -33,41 +38,83 @@ rescue Slop::UnknownOption => e
|
|
|
33
38
|
exit
|
|
34
39
|
end
|
|
35
40
|
|
|
36
|
-
logger
|
|
41
|
+
def logger
|
|
42
|
+
@logger ||= build_logger_from @opts
|
|
43
|
+
end
|
|
37
44
|
|
|
38
45
|
unless Msp430Bsl::Configs::BAUD_RATES.include? @opts[:baud]
|
|
39
46
|
logger.error "BAUD rate #{@opts[:baud]} not supported. Available BAUD rates: #{ Msp430Bsl::Configs::BAUD_RATES.join ', ' }"
|
|
40
47
|
exit
|
|
41
48
|
end
|
|
42
49
|
|
|
50
|
+
# If password param has been provided, convert given string
|
|
51
|
+
if @opts[:password] && !@opts[:password].eql?(Msp430Bsl::Configs::CMD_RX_PASSWORD)
|
|
52
|
+
@opts[:password] = @opts[:password].scan(EXTRACT_PASS_FROM_FILE_REGEX).flatten.compact.join.to_hex_ary
|
|
53
|
+
unless @opts[:password].any?
|
|
54
|
+
logger.error "Wrong password provided. Please copy entirely hex file rows staring with :10FFE0 and :10FFF0"
|
|
55
|
+
exit
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# If extract_pass_from param has been provided, extract BSL password from given file path
|
|
60
|
+
if @opts[:extract_pass_from]
|
|
61
|
+
logger.info "Extracting BSL password from #{@opts[:extract_pass_from]}"
|
|
62
|
+
file_content = File.read @opts[:extract_pass_from]
|
|
63
|
+
lines = file_content.scan(EXTRACT_PASS_FROM_FILE_REGEX).flatten.compact
|
|
64
|
+
@opts[:password] = lines.map { |line| line.scan(/.{2}/).map { |el| el.to_i 16 }}.flatten
|
|
65
|
+
logger.debug "BSL pass: #{@opts[:password].to_hex.join}"
|
|
66
|
+
end
|
|
67
|
+
|
|
43
68
|
# Build UART Connection
|
|
44
69
|
@board = Msp430Bsl::Uart::Connection.new @opts[:device], logger: logger
|
|
45
70
|
|
|
46
71
|
# Enter BSL
|
|
47
72
|
@board.enter_bsl
|
|
48
73
|
# Mass erase FLASH
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
74
|
+
if !@opts[:no_mass_erase]
|
|
75
|
+
logger.info 'Mass erasing FLASH'
|
|
76
|
+
@board.send_command :mass_erase
|
|
77
|
+
end
|
|
52
78
|
logger.info "Unlocking BSL's password protected commands"
|
|
53
|
-
@board.send_command :rx_password, data:
|
|
79
|
+
@board.send_command :rx_password, data: @opts[:password]
|
|
80
|
+
|
|
54
81
|
# Switch UART to max speed
|
|
55
82
|
logger.info "Changing UART BAUD to #{@opts[:baud]}"
|
|
56
83
|
@board.send_command :change_baud_rate, data: Msp430Bsl::Configs::BAUD_RATES[@opts[:baud]]
|
|
57
84
|
@board.set_uart_speed @opts[:baud]
|
|
58
85
|
|
|
59
86
|
# If everything has gone well so far...
|
|
60
|
-
hexfile = Msp430Bsl::HexFile.
|
|
87
|
+
hexfile = Msp430Bsl::HexFile.load @opts[:hexfile]
|
|
61
88
|
|
|
62
89
|
# Group lines by contiguous memory addr
|
|
63
90
|
logger.info 'Writing data to FLASH'
|
|
64
91
|
line_groups = hexfile.data_lines_grouped_by_contiguous_addr
|
|
65
92
|
|
|
93
|
+
@erased_segments = []
|
|
66
94
|
def write_data_packet(lines_packet, check: true)
|
|
67
95
|
attempts = 0
|
|
68
96
|
loop do
|
|
69
|
-
|
|
70
|
-
|
|
97
|
+
addr = lines_packet.first.addr
|
|
98
|
+
data = lines_packet.map { |line| line.data }.reduce(:+)
|
|
99
|
+
if @opts[:no_mass_erase]
|
|
100
|
+
starting_segment_addr = (addr / 0x200) * 0x200
|
|
101
|
+
ending_segment_addr = ((addr + data.size - 1) / 0x200) * 0x200
|
|
102
|
+
logger.debug "Starting segment addr: #{starting_segment_addr.to_hex_str} - Must erase: #{!@erased_segments.include? starting_segment_addr }"
|
|
103
|
+
logger.debug "Ending segment addr: #{ending_segment_addr.to_hex_str} - Must erase: #{!@erased_segments.include? ending_segment_addr}"
|
|
104
|
+
if !@erased_segments.include? starting_segment_addr
|
|
105
|
+
@board.send_command(:erase_segment, addr: starting_segment_addr)
|
|
106
|
+
end
|
|
107
|
+
if !@erased_segments.include? ending_segment_addr
|
|
108
|
+
@board.send_command(:erase_segment, addr: ending_segment_addr)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
@erased_segments << starting_segment_addr
|
|
112
|
+
@erased_segments << ending_segment_addr
|
|
113
|
+
@erased_segments.uniq!
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Write data to FLASH*
|
|
117
|
+
resp = @board.send_command :rx_data_block, addr: addr, data: data
|
|
71
118
|
|
|
72
119
|
break resp unless check
|
|
73
120
|
# Check written data
|
|
@@ -92,26 +139,32 @@ def write_data_packet(lines_packet, check: true)
|
|
|
92
139
|
end
|
|
93
140
|
end
|
|
94
141
|
|
|
142
|
+
def is_there_still_room_for?(next_line, into:) # :into must be the curr_data_packet
|
|
143
|
+
pure_data_length = into.sum { |line| line.data_length } + next_line.data_length
|
|
144
|
+
return false if pure_data_length > Msp430Bsl::Configs::PURE_DATA_MAX_SIZE
|
|
145
|
+
|
|
146
|
+
total_packet_length = pure_data_length + Msp430Bsl::Uart::PeripheralInterface::TOTAL_SIZE
|
|
147
|
+
return false if total_packet_length > Msp430Bsl::Configs::CORE_COMMANDS_BUFFER_SIZE
|
|
148
|
+
|
|
149
|
+
true
|
|
150
|
+
end
|
|
151
|
+
|
|
95
152
|
# Try to optimize BSL writes
|
|
96
153
|
# For each lines group, append as many lines as possible, given the BSL Core Commands buffer size
|
|
97
154
|
line_groups.each do |group|
|
|
98
155
|
curr_data_packet = []
|
|
99
|
-
curr_data_size = 0
|
|
100
156
|
# Cycle lines in a group
|
|
101
157
|
group.each do |line|
|
|
102
158
|
if curr_data_packet.empty?
|
|
103
159
|
# Use current line's addr as packet addr
|
|
104
160
|
curr_data_packet << line
|
|
105
|
-
|
|
106
|
-
elsif (curr_data_size + line.data_length) <= Msp430Bsl::Uart::Connection::CORE_COMMANDS_BUFFER_SIZE
|
|
161
|
+
elsif is_there_still_room_for?(line, into: curr_data_packet)
|
|
107
162
|
# If there's still room, append the line data
|
|
108
163
|
curr_data_packet << line
|
|
109
|
-
curr_data_size += line.data_length
|
|
110
164
|
else
|
|
111
165
|
# No room left, send packet
|
|
112
166
|
write_data_packet curr_data_packet, check: @opts[:check]
|
|
113
167
|
curr_data_packet = []
|
|
114
|
-
curr_data_size = 0
|
|
115
168
|
redo # Handle current line than would otherwise be skipped
|
|
116
169
|
end
|
|
117
170
|
end
|
|
@@ -122,8 +175,11 @@ line_groups.each do |group|
|
|
|
122
175
|
end
|
|
123
176
|
end
|
|
124
177
|
|
|
125
|
-
#
|
|
126
|
-
|
|
178
|
+
# Reset board if requested to
|
|
179
|
+
if @opts[:reset]
|
|
180
|
+
logger.info "Resetting board"
|
|
181
|
+
@board.trigger_reset
|
|
182
|
+
end
|
|
127
183
|
logger.info 'Closing connection'
|
|
128
184
|
@board.close_connection
|
|
129
185
|
logger.info 'Upload done!'
|
data/lib/core_ext/number.rb
CHANGED
|
@@ -7,9 +7,16 @@ class Numeric
|
|
|
7
7
|
n
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def to_bytes_ary
|
|
10
|
+
def to_bytes_ary(le: true, padding: 2)
|
|
11
11
|
res = []
|
|
12
12
|
to_hex_str.chars.each_slice(2) { |byte| res << byte.join().to_i(16) }
|
|
13
|
+
if res.size < padding
|
|
14
|
+
res.prepend Array.new(padding - res.size) { 0 }
|
|
15
|
+
res.flatten!
|
|
16
|
+
end
|
|
17
|
+
if le
|
|
18
|
+
res.reverse!
|
|
19
|
+
end
|
|
13
20
|
res
|
|
14
21
|
end
|
|
15
22
|
|
data/lib/msp430_bsl/command.rb
CHANGED
|
@@ -33,14 +33,11 @@ module Msp430Bsl
|
|
|
33
33
|
@packet = [code, splitted_addr, data].flatten.compact
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def length
|
|
37
|
-
code.to_bytes_ary.length
|
|
38
|
-
end
|
|
39
|
-
|
|
40
36
|
# Split address to [low, middle, high] bytes
|
|
41
37
|
def splitted_addr
|
|
42
38
|
if addr
|
|
43
|
-
[ (addr & 0xFF), ((addr >> 8) & 0xFF), ((addr >> 16) & 0xFF) ]
|
|
39
|
+
# [ (addr & 0xFF), ((addr >> 8) & 0xFF), ((addr >> 16) & 0xFF) ]
|
|
40
|
+
addr.to_bytes_ary padding: 3
|
|
44
41
|
end
|
|
45
42
|
end
|
|
46
43
|
|
data/lib/msp430_bsl/configs.rb
CHANGED
|
@@ -44,5 +44,13 @@ module Msp430Bsl
|
|
|
44
44
|
57600 => 0x05,
|
|
45
45
|
115200 => 0x06
|
|
46
46
|
}.freeze
|
|
47
|
+
|
|
48
|
+
CORE_COMMANDS_BUFFER_SIZE = 260
|
|
49
|
+
PURE_DATA_MAX_SIZE = 250
|
|
50
|
+
|
|
51
|
+
def self.reponse_reason_by_code(code)
|
|
52
|
+
message = RESPONSE_MESSAGES.find { |message, data| data[:code] == code }
|
|
53
|
+
message && message.last[:reason]
|
|
54
|
+
end
|
|
47
55
|
end
|
|
48
56
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Msp430Bsl
|
|
2
|
+
class DataFile
|
|
3
|
+
|
|
4
|
+
LINE_DATA_SIZE = Configs::PURE_DATA_MAX_SIZE
|
|
5
|
+
|
|
6
|
+
attr_reader :path, :raw_data, :lines
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@lines = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_new_lines_from(data, starting_addr)
|
|
13
|
+
curr_addr = starting_addr
|
|
14
|
+
data.each_slice(line_data_size) do |slice|
|
|
15
|
+
@lines << new_line_from(slice, curr_addr)
|
|
16
|
+
curr_addr += line_data_size
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def <<(line)
|
|
21
|
+
@lines << line
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def line_data_size
|
|
25
|
+
self.class::LINE_DATA_SIZE
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def new_line_from(data, addr, type: :data)
|
|
29
|
+
raise NotImplementedError, 'You must implement this method in a subclass'
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/msp430_bsl/hex_file.rb
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
module Msp430Bsl
|
|
2
|
-
class HexFile
|
|
2
|
+
class HexFile < DataFile
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
LINE_DATA_SIZE = 0x10.freeze
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@lines = []
|
|
15
|
-
raw_data.each_line.with_index { |line, i| @lines << HexLine.new(line, num: i) }
|
|
16
|
-
@lines
|
|
6
|
+
class << self
|
|
7
|
+
def load(path)
|
|
8
|
+
path = File.expand_path path
|
|
9
|
+
raw_data = File.read path
|
|
10
|
+
hexfile = new
|
|
11
|
+
hexfile.load_lines_from raw_data
|
|
12
|
+
hexfile
|
|
13
|
+
end
|
|
17
14
|
end
|
|
18
15
|
|
|
19
16
|
def data_lines_grouped_by_contiguous_addr
|
|
@@ -47,5 +44,28 @@ module Msp430Bsl
|
|
|
47
44
|
|
|
48
45
|
@grouped_lines
|
|
49
46
|
end
|
|
47
|
+
|
|
48
|
+
def load_lines_from(raw_data)
|
|
49
|
+
raw_data.each_line.with_index do |line, i|
|
|
50
|
+
line.strip!
|
|
51
|
+
next if line.empty?
|
|
52
|
+
|
|
53
|
+
@lines << HexLine.new(line, num: i)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def raw_data
|
|
58
|
+
@lines.map { |l| l.data.to_hex }.join("\n")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def new_line_from(data, addr, type: :data)
|
|
62
|
+
data_str = "#{data.size.to_hex_str}#{addr.to_hex_str}#{HexLine::RECORD_TYPES[type].to_hex_str}#{data.to_hex.join}"
|
|
63
|
+
data_str = ":#{data_str}#{crc8(data_str.to_hex_ary).to_hex_str}"
|
|
64
|
+
HexLine.new data_str
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def to_s
|
|
68
|
+
@lines.map(&:to_s).join("\n")
|
|
69
|
+
end
|
|
50
70
|
end
|
|
51
71
|
end
|
data/lib/msp430_bsl/hex_line.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module Msp430Bsl
|
|
4
4
|
class HexLine
|
|
5
5
|
include Utils
|
|
6
|
+
extend Utils
|
|
6
7
|
|
|
7
8
|
attr_reader :raw_data, :data_length, :addr, :type, :data, :crc, :number, :end_addr
|
|
8
9
|
|
|
@@ -19,12 +20,21 @@ module Msp430Bsl
|
|
|
19
20
|
raise StandardError, 'raw_data must be a String' unless data.is_a?(String)
|
|
20
21
|
|
|
21
22
|
# Strip String, remove first char i.e. ':' and convert char couples to its hex value.
|
|
22
|
-
|
|
23
|
+
if data[0] == ':'
|
|
24
|
+
@raw_data = data.strip[1..-1]
|
|
25
|
+
end
|
|
26
|
+
# Convert raw data to hex array
|
|
27
|
+
@raw_data = @raw_data.to_hex_ary
|
|
23
28
|
|
|
29
|
+
# Extract data length
|
|
24
30
|
@data_length = raw_data[0]
|
|
31
|
+
# Extract addr
|
|
25
32
|
@addr = (raw_data[1] << 8) | raw_data[2]
|
|
33
|
+
# Extract line type
|
|
26
34
|
@type = raw_data[3]
|
|
35
|
+
# Extract data
|
|
27
36
|
@data = raw_data.slice 4, data_length
|
|
37
|
+
# Extract CRC
|
|
28
38
|
@crc = raw_data[-1]
|
|
29
39
|
@number = num
|
|
30
40
|
@end_addr = addr + data_length
|
|
@@ -44,5 +54,9 @@ module Msp430Bsl
|
|
|
44
54
|
ty = ty.to_sym
|
|
45
55
|
type == RECORD_TYPES[ty]
|
|
46
56
|
end
|
|
57
|
+
|
|
58
|
+
def to_s
|
|
59
|
+
":#{@data_length.to_hex_str}#{@addr.to_hex_str}#{@type.to_hex_str}#{data.to_hex.join}#{@crc.to_hex_str}"
|
|
60
|
+
end
|
|
47
61
|
end
|
|
48
62
|
end
|
data/lib/msp430_bsl/response.rb
CHANGED
|
@@ -53,11 +53,15 @@ module Msp430Bsl
|
|
|
53
53
|
if is_message?
|
|
54
54
|
success_code = Configs::RESPONSE_MESSAGES[:success][:code]
|
|
55
55
|
if data[0] != success_code # First (and only) data byte is message code
|
|
56
|
-
@errors << [:message_code, "Message code NOK. Expected message code '#{success_code}', got '#{data[0]}'"]
|
|
56
|
+
@errors << [:message_code, "Message code NOK. Expected message code '#{success_code}', got '#{data[0]}' that should mean: '#{Configs.reponse_reason_by_code(data[0])}'"]
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
@errors.empty?
|
|
61
61
|
end
|
|
62
|
+
|
|
63
|
+
def inspect
|
|
64
|
+
data.to_hex
|
|
65
|
+
end
|
|
62
66
|
end
|
|
63
67
|
end
|
|
@@ -11,19 +11,15 @@ module Msp430Bsl
|
|
|
11
11
|
WAIT_FOR_ACK_MAX = 1000.millis
|
|
12
12
|
WAIT_FOR_RESPONSE_MAX = 1000.millis
|
|
13
13
|
|
|
14
|
-
MEM_START_MAIN_FLASH = 0x8000
|
|
15
|
-
|
|
16
|
-
CORE_COMMANDS_BUFFER_SIZE = 260
|
|
17
|
-
|
|
18
14
|
attr_reader :serial_port, :device_path, :logger, :cmd_buff_size
|
|
19
15
|
|
|
20
16
|
def initialize(device_path, opts = {})
|
|
21
17
|
@device_path = device_path
|
|
22
18
|
@logger = opts.fetch :logger, Logger.new(STDOUT)
|
|
23
|
-
@cmd_buff_size = opts.fetch :cmd_buf_size, CORE_COMMANDS_BUFFER_SIZE
|
|
19
|
+
@cmd_buff_size = opts.fetch :cmd_buf_size, Configs::CORE_COMMANDS_BUFFER_SIZE
|
|
24
20
|
|
|
25
21
|
@serial_port = SerialPort.new @device_path
|
|
26
|
-
@serial_port.flow_control = SerialPort::
|
|
22
|
+
@serial_port.flow_control = SerialPort::SOFT
|
|
27
23
|
end
|
|
28
24
|
|
|
29
25
|
def close_connection
|
|
@@ -92,7 +88,7 @@ module Msp430Bsl
|
|
|
92
88
|
response
|
|
93
89
|
end
|
|
94
90
|
|
|
95
|
-
def send_command(cmd_name, addr: nil, data: nil
|
|
91
|
+
def send_command(cmd_name, addr: nil, data: nil)
|
|
96
92
|
command = Command.new cmd_name, addr: addr, data: data
|
|
97
93
|
pi = PeripheralInterface.wrap command
|
|
98
94
|
logger.debug "Sending command '#{command.name}' over UART"
|
|
@@ -102,18 +98,17 @@ module Msp430Bsl
|
|
|
102
98
|
|
|
103
99
|
unless pi.valid?
|
|
104
100
|
logger.error "PeripheralInterface not valid. Errors: #{pi.errors}"
|
|
105
|
-
|
|
101
|
+
raise Exceptions::PeripheralInterface::NotValid, pi.errors
|
|
106
102
|
end
|
|
107
103
|
|
|
108
104
|
logger.debug "OUT -> (#{pi.packet.size} bytes) #{pi.to_hex_ary_str}"
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
end
|
|
105
|
+
|
|
106
|
+
serial_port.write pi.to_uart
|
|
107
|
+
read_response_for command
|
|
113
108
|
end
|
|
114
109
|
|
|
115
110
|
def set_uart_speed(baud)
|
|
116
|
-
raise StandardError, "BAUD not supported. Supported
|
|
111
|
+
raise StandardError, "BAUD not supported. Supported BAUDs: #{Configs::BAUD_RATES.keys}" unless Configs::BAUD_RATES.keys.include?(baud)
|
|
117
112
|
|
|
118
113
|
logger.debug "Setting serial port BAUD to #{baud} bps"
|
|
119
114
|
|
|
@@ -123,7 +118,7 @@ module Msp430Bsl
|
|
|
123
118
|
end
|
|
124
119
|
|
|
125
120
|
def trigger_reset
|
|
126
|
-
#
|
|
121
|
+
# slau319af.pdf - pag. 5 - Fig. 1-1
|
|
127
122
|
reset_pin_go :low
|
|
128
123
|
test_pin_go :low
|
|
129
124
|
sleep 5.millis
|
|
@@ -24,35 +24,41 @@ module Msp430Bsl
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
module PeripheralInterface
|
|
27
|
-
class
|
|
27
|
+
class NotValid < StandardError
|
|
28
|
+
def initialize(errors)
|
|
29
|
+
message = "PeripheralInterface not valid. Errors: '#{errors}'"
|
|
30
|
+
super(message)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
class DoesNotWrapACommand < StandardError
|
|
28
34
|
def initialize(arg)
|
|
29
35
|
message = "Given argument '#{arg}' must be a Msp430Bsl::Command"
|
|
30
36
|
super(message)
|
|
31
37
|
end
|
|
32
38
|
end
|
|
33
39
|
|
|
34
|
-
class
|
|
40
|
+
class ParsedRawDataAreNotAnArray < StandardError
|
|
35
41
|
def initialize
|
|
36
42
|
message = "PeripheralInterface#parse argument must be an Array"
|
|
37
43
|
super(message)
|
|
38
44
|
end
|
|
39
45
|
end
|
|
40
46
|
|
|
41
|
-
class
|
|
47
|
+
class DataAreNotAnArray < StandardError
|
|
42
48
|
def initialize
|
|
43
49
|
message = "Peripheral Interface 'data' argument must be an Array"
|
|
44
50
|
super(message)
|
|
45
51
|
end
|
|
46
52
|
end
|
|
47
53
|
|
|
48
|
-
class
|
|
54
|
+
class DataSizeError < StandardError
|
|
49
55
|
def initialize(received_size)
|
|
50
56
|
message = "Peripheral Interface size error. Required packet's min size is '#{PeripheralInterface::MIN_PACKET_SIZE}' bytes, given raw_data size is '#{received_size}' bytes"
|
|
51
57
|
super(message)
|
|
52
58
|
end
|
|
53
59
|
end
|
|
54
60
|
|
|
55
|
-
class
|
|
61
|
+
class HeaderNOK < StandardError
|
|
56
62
|
def initialize(received_header)
|
|
57
63
|
message = "Peripheral interface header NOK. Received '0x#{received_header.to_hex_str}' instead of 0x#{PeripheralInterface::OK_HEADER.to_hex_str}"
|
|
58
64
|
super(message)
|
|
@@ -60,7 +66,7 @@ module Msp430Bsl
|
|
|
60
66
|
|
|
61
67
|
end
|
|
62
68
|
|
|
63
|
-
class
|
|
69
|
+
class CRCMismatch < StandardError
|
|
64
70
|
def initialize
|
|
65
71
|
message = 'Peripheral Interface CRC mismatch'
|
|
66
72
|
super(message)
|
|
@@ -18,15 +18,15 @@ module Msp430Bsl
|
|
|
18
18
|
|
|
19
19
|
def wrap(command)
|
|
20
20
|
unless command.is_a?(Command)
|
|
21
|
-
raise Exceptions::
|
|
21
|
+
raise Exceptions::PeripheralInterface::DoesNotWrapACommand, command
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
new header: OK_HEADER, data_len: command.packet.size, data: command.packet
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def parse(raw_data)
|
|
28
|
-
raise Exceptions::
|
|
29
|
-
raise Exceptions::
|
|
28
|
+
raise Exceptions::PeripheralInterface::ParsedRawDataAreNotAnArray unless raw_data.is_a?(Array)
|
|
29
|
+
raise Exceptions::PeripheralInterface::DataSizeError, raw_data.size unless raw_data.size >= MIN_PACKET_SIZE
|
|
30
30
|
|
|
31
31
|
header = raw_data[0]
|
|
32
32
|
data_len = raw_data[2] << 8 | raw_data[1]
|
|
@@ -40,7 +40,7 @@ module Msp430Bsl
|
|
|
40
40
|
attr_reader :header, :data_len, :data, :crc, :errors, :packet, :cmd_kind
|
|
41
41
|
|
|
42
42
|
def initialize(header: nil, data_len: nil, data: nil, crc: nil)
|
|
43
|
-
raise Exceptions::
|
|
43
|
+
raise Exceptions::PeripheralInterface::DataAreNotAnArray if (data && !data.is_a?(Array))
|
|
44
44
|
|
|
45
45
|
@header = header
|
|
46
46
|
@data_len = data_len
|
data/lib/msp430_bsl/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: msp430_bsl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alessandro Verlato
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: zeitwerk
|
|
@@ -30,14 +29,14 @@ dependencies:
|
|
|
30
29
|
requirements:
|
|
31
30
|
- - '='
|
|
32
31
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 1.
|
|
32
|
+
version: 1.4.0
|
|
34
33
|
type: :runtime
|
|
35
34
|
prerelease: false
|
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
36
|
requirements:
|
|
38
37
|
- - '='
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: 1.
|
|
39
|
+
version: 1.4.0
|
|
41
40
|
- !ruby/object:Gem::Dependency
|
|
42
41
|
name: slop
|
|
43
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -59,23 +58,24 @@ dependencies:
|
|
|
59
58
|
- - '='
|
|
60
59
|
- !ruby/object:Gem::Version
|
|
61
60
|
version: 0.14.2
|
|
62
|
-
type: :
|
|
61
|
+
type: :development
|
|
63
62
|
prerelease: false
|
|
64
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
64
|
requirements:
|
|
66
65
|
- - '='
|
|
67
66
|
- !ruby/object:Gem::Version
|
|
68
67
|
version: 0.14.2
|
|
69
|
-
description:
|
|
70
68
|
email:
|
|
71
69
|
- averlato@gmail.com
|
|
72
70
|
executables:
|
|
71
|
+
- dump_flash
|
|
73
72
|
- upload_hex
|
|
74
73
|
extensions: []
|
|
75
74
|
extra_rdoc_files: []
|
|
76
75
|
files:
|
|
77
76
|
- MIT-LICENSE
|
|
78
77
|
- README.md
|
|
78
|
+
- bin/dump_flash
|
|
79
79
|
- bin/upload_hex
|
|
80
80
|
- lib/core_ext/array.rb
|
|
81
81
|
- lib/core_ext/number.rb
|
|
@@ -83,9 +83,11 @@ files:
|
|
|
83
83
|
- lib/msp430_bsl.rb
|
|
84
84
|
- lib/msp430_bsl/command.rb
|
|
85
85
|
- lib/msp430_bsl/configs.rb
|
|
86
|
+
- lib/msp430_bsl/data_file.rb
|
|
86
87
|
- lib/msp430_bsl/exceptions.rb
|
|
87
88
|
- lib/msp430_bsl/hex_file.rb
|
|
88
89
|
- lib/msp430_bsl/hex_line.rb
|
|
90
|
+
- lib/msp430_bsl/raw_data_file.rb
|
|
89
91
|
- lib/msp430_bsl/response.rb
|
|
90
92
|
- lib/msp430_bsl/uart/ack.rb
|
|
91
93
|
- lib/msp430_bsl/uart/connection.rb
|
|
@@ -97,7 +99,6 @@ homepage: https://github.com/madAle/msp430_bsl
|
|
|
97
99
|
licenses:
|
|
98
100
|
- MIT
|
|
99
101
|
metadata: {}
|
|
100
|
-
post_install_message:
|
|
101
102
|
rdoc_options: []
|
|
102
103
|
require_paths:
|
|
103
104
|
- lib
|
|
@@ -112,8 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
112
113
|
- !ruby/object:Gem::Version
|
|
113
114
|
version: '0'
|
|
114
115
|
requirements: []
|
|
115
|
-
rubygems_version: 3.
|
|
116
|
-
signing_key:
|
|
116
|
+
rubygems_version: 3.6.9
|
|
117
117
|
specification_version: 4
|
|
118
118
|
summary: Texas Instrument MSP430 BSL Ruby library
|
|
119
119
|
test_files: []
|