libfst 0.1.0

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.
@@ -0,0 +1,793 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright (C) 2024 Théotime Bollengier <theotime.bollengier@ensta-bretagne.fr>
4
+ #
5
+ # This file is part of libfst.rb <https://gitlab.ensta-bretagne.fr/bollenth/libfst.rb>
6
+ #
7
+ # libfst.rb is free software: you can redistribute it and/or modify it
8
+ # under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License,
10
+ # or (at your option) any later version.
11
+ #
12
+ # libfst.rb is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
+ # See the GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with libfst.rb. If not, see <https://www.gnu.org/licenses/>. 
19
+
20
+ require_relative '../../lib/libfst'
21
+ require 'colorize'
22
+
23
+ class SDCardDecoder < GTKWave::TransactionFilter
24
+ CMDS = [
25
+ # name, response, abbrev, argument
26
+ ['CMD0', nil, 'GO_IDLE_STATE'],
27
+ ['CMD1', nil, 'RESERVED'],
28
+ ['CMD2', :R2, 'ALL_SEND_CID'],
29
+ ['CMD3', :R6, 'SEND_RELATIVE_ADDR'],
30
+ ['CMD4', nil, 'SET_DSR', { (16..31) => 'DSR' }],
31
+ ['CMD5', nil, 'RESERVED'],
32
+ ['CMD6', :R1, 'SWITCH_FUNCTION', { (0..3) => 'fg1am', (4..7) => 'fg2cs', (8..11) => 'fg3ds', (12..15) => 'fg4pl', (31..31) => 'check or switch function' }],
33
+ ['CMD7', :R1b, 'SELECT/DESELECT_CARD', { (16..31) => 'RCA' }],
34
+ ['CMD8', :R7, 'SEND_IF_COND', { (0..7) => 'check pattern', (8..11) => 'supply voltage (VHS)' }],
35
+ ['CMD9', :R2, 'SEND_CSD', { (16..31) => 'RCA' }],
36
+ ['CMD10', :R2, 'SEND_CID', { (16..31) => 'RCA' }],
37
+ ['CMD11', :R1, 'VOLTAGE_SWITCH'],
38
+ ['CMD12', :R1b, 'STOP_TRANSMISSION'],
39
+ ['CMD13', :R1, 'SEND_STATUS', { (16..31) => 'RCA' }],
40
+ ['CMD14', nil, 'RESERVED'],
41
+ ['CMD15', nil, 'GO_INACTIVE_STATE', { (16..31) => 'RCA' }],
42
+ ['CMD16', :R1, 'SET_BLOCKLEN', { (0..31) => 'block length' }],
43
+ ['CMD17', :R1, 'READ_SINGLE_BLOCK', { (0..31) => 'data address' }],
44
+ ['CMD18', :R1, 'READ_MULTIPLE_BLOCK', { (0..31) => 'data address' }],
45
+ ['CMD19', :R1, 'SEND_TUNING_BLOCK'],
46
+ ['CMD20', :R1b, 'SPEED_CLASS_CONTROL', { (0..27) => 'speed class', (28..31) => 'speed class control' }],
47
+ ['CMD21'],
48
+ ['CMD22', :R1, 'ADDRESS_EXTENSION', { (0..5) => 'extended address' }],
49
+ ['CMD23', :R1, 'SET_BLOCK_COUNT', { (0..31) => 'block count' }],
50
+ ['CMD24', :R1, 'WRITE_BLOCK', { (0..31) => 'data address' }],
51
+ ['CMD25', :R1, 'WRITE_MULTIPLE_BLOCK', { (0..31) => 'data address' }],
52
+ ['CMD26', nil, 'RESERVED'],
53
+ ['CMD27', :R1, 'PROGRAM_CSD'],
54
+ ['CMD28', :R1b, 'SET_WRITE_PROT', { (0..31) => 'data address' }],
55
+ ['CMD29', :R1b, 'CLR_WRITE_PROT', { (0..31) => 'data address' }],
56
+ ['CMD30', :R1, 'SEND_WRITE_PROT', { (0..31) => 'write protect data address' }],
57
+ ['CMD31', nil, 'RESERVED'],
58
+ ['CMD32', :R1, 'ERASE_WR_BLK_START', { (0..31) => 'data address' }],
59
+ ['CMD33', :R1, 'ERASE_WR_BLK_END', { (0..31) => 'data address' }],
60
+ ['CMD34'],
61
+ ['CMD35'],
62
+ ['CMD36'],
63
+ ['CMD37'],
64
+ ['CMD38', :R1b, 'ERASE', { (0..31) => 'erase function' }],
65
+ ['CMD39', :R1b, 'SELECT_CARD_PARTITION', { (24..31) => 'partition ID' }],
66
+ ['CMD40', :R1, 'def_by_dps_spec', { (0..31) => 'def by DPS spec' }],
67
+ ['CMD41', :R3, 'RESERVED'],
68
+ ['CMD42', :R1, 'LOCK_UNLOCK', { (0..31) => 'def by DPS spec' }],
69
+ ['CMD43', :R1b, 'Q_MANAGEMENT', { (0..3) => 'opcode', (16..20) => 'task ID' }],
70
+ ['CMD44', :R1, 'Q_TASK_INFO_A', { (0..15) => 'number of blocks', (16..20) => 'task ID', (23..23) => 'priority', (24..29) => 'extended address', (30..30) => 'direction' }],
71
+ ['CMD45', :R1, 'Q_TASK_INFO_B', { (0..31) => 'start block address' }],
72
+ ['CMD46', :R1, 'Q_RD_TASK', { (16..20) => 'task ID' }],
73
+ ['CMD47', :R1, 'Q_WR_TASK', { (16..20) => 'task ID' }],
74
+ ['CMD48', :R1, 'READ_EXTR_SINGLE', { (0..8) => 'LEN', (9..25) => 'ADDR', (27..30) => 'FNO', (31..31) => 'memory or IO' }],
75
+ ['CMD49', :R1, 'WRITE_EXTR_SINGLE', { (0..8) => 'LEN/MASK', (9..25) => 'ADDR', (27..30) => 'FNO', (31..31) => 'memory or IO' }],
76
+ ['CMD50'],
77
+ ['CMD51', nil, 'RESERVED'],
78
+ ['CMD52'],
79
+ ['CMD53'],
80
+ ['CMD54'],
81
+ ['CMD55', :R1, 'APP_CMD', { (16..31) => 'RCA' }], # next command is a ACMD
82
+ ['CMD56', :R1, 'GEN_CMD', { (0..0) => 'RD/WR' }],
83
+ ['CMD57'],
84
+ ['CMD58', :R1, 'READ_EXTR_MULTI', { (0..8) => 'BUC', (9..25) => 'ADDR', (26..26) => 'BUS', (27..30) => 'FNO', (31..31) => 'memory or IO' }],
85
+ ['CMD59', :R1, 'WRITE_EXTR_MULTI', { (0..8) => 'BUC', (9..25) => 'ADDR', (26..26) => 'BUS', (27..30) => 'FNO', (31..31) => 'memory or IO' }],
86
+ ['CMD60'],
87
+ ['CMD61'],
88
+ ['CMD62'],
89
+ ['CMD63']
90
+ ].freeze
91
+
92
+ ACMDS = [
93
+ ['ACMD0'],
94
+ ['ACMD1', nil, 'RESERVED'],
95
+ ['ACMD2', nil, 'RESERVED'],
96
+ ['ACMD3', nil, 'RESERVED'],
97
+ ['ACMD4', nil, 'RESERVED'],
98
+ ['ACMD5', nil, 'RESERVED'],
99
+ ['ACMD6', :R1, 'SET_BUS_WIDTH', { (0..1) => 'bus width' }],
100
+ ['ACMD7', nil, 'RESERVED'],
101
+ ['ACMD8', nil, 'RESERVED'],
102
+ ['ACMD9', nil, 'RESERVED'],
103
+ ['ACMD10', nil, 'RESERVED'],
104
+ ['ACMD11', nil, 'RESERVED'],
105
+ ['ACMD12', nil, 'RESERVED'],
106
+ ['ACMD13', :R1, 'SD_STATUS'],
107
+ ['ACMD14', nil, 'RESERVED'],
108
+ ['ACMD15', nil, 'RESERVED'],
109
+ ['ACMD16', nil, 'RESERVED'],
110
+ ['ACMD17', nil, 'RESERVED'],
111
+ ['ACMD18', nil, 'RESERVED'],
112
+ ['ACMD19', nil, 'RESERVED'],
113
+ ['ACMD20', nil, 'RESERVED'],
114
+ ['ACMD21', nil, 'RESERVED'],
115
+ ['ACMD22', :R1, 'SEND_NUM_WR_BLOCKS'],
116
+ ['ACMD23', :R1, 'SEND_WR_BLK_ERASE_COUNT', { (0..22) => 'number of blocks' }],
117
+ ['ACMD24', nil, 'RESERVED'],
118
+ ['ACMD25', nil, 'RESERVED'],
119
+ ['ACMD26', nil, 'RESERVED'],
120
+ ['ACMD27', nil, 'RESERVED'],
121
+ ['ACMD28', nil, 'RESERVED'],
122
+ ['ACMD29', nil, 'RESERVED'],
123
+ ['ACMD30', nil, 'RESERVED'],
124
+ ['ACMD31', nil, 'RESERVED'],
125
+ ['ACMD32', nil, 'RESERVED'],
126
+ ['ACMD33', nil, 'RESERVED'],
127
+ ['ACMD34', nil, 'RESERVED'],
128
+ ['ACMD35', nil, 'RESERVED'],
129
+ ['ACMD36', nil, 'RESERVED'],
130
+ ['ACMD37', nil, 'RESERVED'],
131
+ ['ACMD38', nil, 'RESERVED'],
132
+ ['ACMD39', nil, 'RESERVED'],
133
+ ['ACMD40', nil, 'RESERVED'],
134
+ ['ACMD41', :R3, 'SD_SEND_OP_COND', { (0..23) => 'VDD voltage window', (24..24) => 'S18R', (28..28) => 'XPC', (30..30) => 'HCS' }],
135
+ ['ACMD42', :R1, 'SET_CLR_CARD_DETECT', { (0..0) => 'set_cd' }],
136
+ ['ACMD43', nil, 'RESERVED'],
137
+ ['ACMD44', nil, 'RESERVED'],
138
+ ['ACMD45', nil, 'RESERVED'],
139
+ ['ACMD46', nil, 'RESERVED'],
140
+ ['ACMD47', nil, 'RESERVED'],
141
+ ['ACMD48', nil, 'RESERVED'],
142
+ ['ACMD49', nil, 'RESERVED'],
143
+ ['ACMD50'],
144
+ ['ACMD51', :R1, 'SEND_SCR'],
145
+ ['ACMD52', nil, 'RESERVED'],
146
+ ['ACMD53', :R1, 'SECURE_RECEIVE', { (0..7) => 'SSSF', (8..15) => 'SPSP0', (16..23) => 'SPSP1', (24..31) => 'Security protocol' }],
147
+ ['ACMD54', :R1, 'SECURE_SEND', { (0..7) => 'SSSF', (8..15) => 'SPSP0', (16..23) => 'SPSP1', (24..31) => 'Security protocol' }],
148
+ ['ACMD55', :R1, 'APP_CMD', { (16..31) => 'RCA' }],
149
+ ['ACMD56', nil, 'RESERVED'],
150
+ ['ACMD57', nil, 'RESERVED'],
151
+ ['ACMD58', nil, 'RESERVED'],
152
+ ['ACMD59', nil, 'RESERVED']
153
+ ].freeze
154
+
155
+
156
+ def initialize
157
+ super
158
+ puts ' SD Card Decoder '.center(80, '-')
159
+ puts "Name: #{@name.inspect}"
160
+ puts "#{@signames.length} signal#{@signames.length > 1 ? 's' : ''}: #{[@signames[0...-1].join(', '), @signames.last].reject(&:empty?).join(' and ')}"
161
+ raise "expecting two signals, not #{@ivcd.traces.length}" unless @ivcd.traces.length == 2
162
+
163
+ # Try to find the clk and cmd signals
164
+ @ivcd.traces.each_value do |t|
165
+ raise "expecting two signals of 1 bit, not #{t.width}-bit wide" unless t.width == 1
166
+ t.variables.each do |v|
167
+ if v.name =~ /cl(oc)?k/i then
168
+ raise 'CLK found twice' if @clk_trace
169
+ @clk_trace = t
170
+ break
171
+ elsif v.name =~ /co?m(man)?d/i then
172
+ raise 'CMD found twice' if @cmd_trace
173
+ @cmd_trace = t
174
+ break
175
+ end
176
+ end
177
+ end
178
+ raise 'Cannot find a clock signal' unless @clk_trace
179
+ raise 'Cannot find a command signal' unless @cmd_trace
180
+ end
181
+
182
+
183
+ def puts(*args)
184
+ $stderr.puts(*args)
185
+ end
186
+
187
+
188
+ def read
189
+ @cmd = 1
190
+ @clk = nil
191
+ @time = nil
192
+ @state = :wait_host_start_bit
193
+ @times = [0]
194
+ @phases = ['']
195
+ @values = ['']
196
+
197
+ @ivcd.read do |trace, value, time|
198
+ case trace
199
+ when @clk_trace
200
+ clock(value, time)
201
+ when @cmd_trace
202
+ @cmd = value.to_i
203
+ end
204
+ end
205
+ end
206
+
207
+
208
+ def write
209
+ $stdout.write "$name #{@name}\n"
210
+ $stdout.write "##{@min_time}\n"
211
+ @times.each_with_index do |t, i|
212
+ v = @values[i]
213
+ $stdout.write "##{t}#{v ? " #{v}" : ''}\n"
214
+ end
215
+ $stdout.write "$next\n"
216
+ $stdout.write "$name desc\n"
217
+ $stdout.write "##{@min_time}\n"
218
+ @times.each_with_index do |t, i|
219
+ p = @phases[i]
220
+ $stdout.write "##{t}#{p ? " #{p}" : ''}\n"
221
+ end
222
+ $stdout.write "$finish\n"
223
+ $stdout.flush
224
+ puts '-'*80
225
+ end
226
+
227
+
228
+ private
229
+
230
+
231
+ def clock(value, time)
232
+ value = value.to_i
233
+ if value == 1 and @clk == 0 then
234
+ rising_edge
235
+ elsif value == 0 and @clk == 1 then
236
+ falling_edge
237
+ end
238
+ @clk = value
239
+ @time = time
240
+ end
241
+
242
+ =begin
243
+ def reset_read
244
+ @read_index = 0
245
+ end
246
+
247
+ def advance
248
+ @read_index += 1
249
+ end
250
+
251
+ def get_time
252
+ @times[@read_index]
253
+ end
254
+
255
+ def get_phase_and_value
256
+ [@phases[@read_index], @values[@read_index]]
257
+ end
258
+ =end
259
+
260
+ def crc7(str)
261
+ crc = 0
262
+ str.each_byte do |b|
263
+ b = (b == 48 ? 0 : 1)
264
+ crc = (b ^ crc[6]) | ((crc[2] ^ (b ^ crc[6])) << 3) | ((crc << 1) & 0x76)
265
+ end
266
+ crc
267
+ end
268
+
269
+ def card_status_to_string(cs)
270
+ # page 147
271
+ a = []
272
+ a << 'OUT_OF_RANGE' if cs[31] == 1
273
+ a << 'ADDRESS_ERROR' if cs[30] == 1
274
+ a << 'BLOCL_LEN_ERROR' if cs[29] == 1
275
+ a << 'ERASE_SEQ_ERROR' if cs[28] == 1
276
+ a << 'ERASE_PARAM' if cs[27] == 1
277
+ a << 'WP_VIOLATION' if cs[26] == 1
278
+ a << 'CARD_IS_LOCKED' if cs[25] == 1
279
+ a << 'LOCK_UNLOCK_FAILED' if cs[24] == 1
280
+ a << 'COM_CRC_ERROR' if cs[23] == 1
281
+ a << 'ILLEGAL_COMMAND' if cs[22] == 1
282
+ a << 'CARD_ECC_FAILED' if cs[21] == 1
283
+ a << 'CC_ERROR' if cs[20] == 1
284
+ a << 'ERROR' if cs[19] == 1
285
+ a << 'CSD_OVERWRITE' if cs[16] == 1
286
+ a << 'WP_ERASE_SKIP' if cs[15] == 1
287
+ a << 'CARD_ECC_DISABLED' if cs[14] == 1
288
+ a << 'ERASE_RESET' if cs[13] == 1
289
+ a << %w[idle ready ident stby tran data rcv prg dis xxx xxx xxx xxx xxx xxx RES][(cs >> 9) & 0x0f]
290
+ a << 'READY_FOR_DATA' if cs[8] == 1
291
+ a << 'FX_EVENT' if cs[6] == 1
292
+ a << 'APP_CMD' if cs[5] == 1
293
+ a << 'AKE_SEQ_ERROR' if cs[3] == 1
294
+ a << 'reserved bits set' if (cs & 0x00060097) != 0
295
+ a.join(', ')
296
+ end
297
+
298
+
299
+ def MID_to_string(mid)
300
+ name = {
301
+ 0x01=>'Panasonic',
302
+ 0x02=>'Toshiba',
303
+ 0x03=>'SanDisk',
304
+ 0x1b=>'Samsung',
305
+ 0x1d=>'AData',
306
+ 0x27=>'Phison',
307
+ 0x28=>'Lexar',
308
+ 0x31=>'Silicon Power',
309
+ 0x41=>'Kingston',
310
+ 0x74=>'Transcend',
311
+ 0x76=>'Patriot',
312
+ 0x82=>'Sony'
313
+ }[mid]
314
+ return '' unless name
315
+ " (#{name})"
316
+ end
317
+
318
+
319
+ def cid_to_s(cid)
320
+ # page 250
321
+ cid <<= 8
322
+ a = []
323
+ mid = (cid >> 120) & 0xff
324
+ a << "MID: 0x#{mid.to_s(16).rjust(2,'0')}#{MID_to_string(mid)}"
325
+ a << "OID: \"#{((cid >> 112) & 0xff).chr}#{((cid >> 104) & 0xff).chr}\""
326
+ pnm = 5.times.reverse_each.collect { |i| ((cid >> (64 + 8*i)) & 0xff).chr }.join
327
+ a << "PNM: \"#{pnm}\""
328
+ a << "PRV: \"#{(cid >> 60) & 0x0f}.#{(cid >> 56) & 0x0f}\""
329
+ a << "PSN: 0x#{((cid >> 24) & 0xffffffff).to_s(16).rjust(8, '0')}"
330
+ mdt = (cid >> 8) & 0xfff
331
+ a << "MDT: #{%w[January February March April May June July August September October November December][(mdt & 0xf) - 1]} #{(mdt >> 4) + 2000}"
332
+ a.join(', ')
333
+ end
334
+
335
+
336
+ def rising_edge
337
+ case @state
338
+ when :r1_start_bit
339
+ if @cmd == 0 then
340
+ @times << @time
341
+ @phases << '?violet?Start bit'
342
+ @values << '?violet?0'
343
+ @bits = @cmd.to_s
344
+ @state = :r1_transmission_bit
345
+ end
346
+ when :r1_transmission_bit
347
+ @times << @time
348
+ @phases << 'Transmission bit'
349
+ @values << @cmd.to_s
350
+ @cnt = 0
351
+ @r_cmd_index = 0
352
+ @bits += @cmd.to_s
353
+ @state = :r1_command_index
354
+ puts 'WARNING: bit is 1 at response transmission bit'.light_red.bold if @cmd == 1
355
+ when :r1_command_index
356
+ if @cnt == 0 then
357
+ @times << @time
358
+ end
359
+ @r_cmd_index = (@r_cmd_index << 1) | @cmd
360
+ @cnt += 1
361
+ if @cnt == 6 then
362
+ @phases << 'CMD index'
363
+ if @r_cmd_index == @cmd_index then
364
+ @values << "#{@r_cmd_index} (OK)"
365
+ else
366
+ @values << "#{@r_cmd_index} (Should be #{@cmd_index})"
367
+ puts "WARNING: R1 response to command #{@r_cmd_index} instead of #{@cmd_index}".light_red.bold
368
+ end
369
+ @cnt = 0
370
+ @card_status = 0
371
+ @state = :r1_status
372
+ end
373
+ @bits += @cmd.to_s
374
+ when :r1_status
375
+ if @cnt == 0 then
376
+ @times << @time
377
+ @phases << 'Card status'
378
+ end
379
+ @card_status = (@card_status << 1) | @cmd
380
+ @cnt += 1
381
+ if @cnt == 32 then
382
+ puts " R1: #{card_status_to_string(@card_status)}"
383
+ @values << card_status_to_string(@card_status)
384
+ @cnt = 0
385
+ @crc = 0
386
+ @state = :r_crc7
387
+ end
388
+ @bits += @cmd.to_s
389
+ when :r_crc7
390
+ if @cnt == 0 then
391
+ @times << @time
392
+ @phases << 'CRC7'
393
+ end
394
+ @crc = (@crc << 1) | @cmd
395
+ @cnt += 1
396
+ if @cnt == 7 then
397
+ if @crc == crc7(@bits) then
398
+ @values << "#{@crc} (OK)"
399
+ else
400
+ puts "WARNING: CRC7(#{@bits}) = #{crc7(@bits)}, got, #{@crc}".light_red.bold
401
+ @values << "#{@crc} (FAIL -> #{crc7(@bits)})"
402
+ end
403
+ @state = :r_end_bit
404
+ end
405
+ when :r_end_bit
406
+ @times << @time
407
+ @phases << 'E'
408
+ @values << @cmd.to_s
409
+ @state = :response_sent
410
+ puts 'WARNING: bit is 0 at end bit'.light_red.bold if @cmd == 0
411
+ when :response_sent
412
+ @times << @time
413
+ @phases << nil
414
+ @values << nil
415
+ @state = :wait_host_start_bit
416
+
417
+ ## R7 ##
418
+ when :r7_start_bit
419
+ if @cmd == 0 then
420
+ @times << @time
421
+ @phases << '?violet?Start bit'
422
+ @values << '?violet?0'
423
+ @bits = @cmd.to_s
424
+ @state = :r7_transmission_bit
425
+ end
426
+ when :r7_transmission_bit
427
+ @times << @time
428
+ @phases << 'Transmission bit'
429
+ @values << @cmd.to_s
430
+ @cnt = 0
431
+ @r_cmd_index = 0
432
+ @bits += @cmd.to_s
433
+ @state = :r7_command_index
434
+ puts 'WARNING: bit is 1 at response transmission bit'.light_red.bold if @cmd == 1
435
+ when :r7_command_index
436
+ if @cnt == 0 then
437
+ @times << @time
438
+ end
439
+ @r_cmd_index = (@r_cmd_index << 1) | @cmd
440
+ @cnt += 1
441
+ if @cnt == 6 then
442
+ @phases << 'CMD index'
443
+ if @r_cmd_index == @cmd_index then
444
+ @values << "#{@r_cmd_index} (OK)"
445
+ else
446
+ @values << "#{@r_cmd_index} (Should be #{@cmd_index})"
447
+ puts "WARNING: R7 response to command #{@r_cmd_index} instead of #{@cmd_index}".light_red.bold
448
+ end
449
+ @cnt = 0
450
+ @tmp = 0
451
+ @state = :r7_reserved
452
+ end
453
+ @bits += @cmd.to_s
454
+ when :r7_reserved
455
+ if @cnt == 0 then
456
+ @times << @time
457
+ @phases << 'Reserved'
458
+ end
459
+ @tmp = (@tmp << 1) | @cmd
460
+ @cnt += 1
461
+ if @cnt == 18 then
462
+ @values << "0x#{@tmp.to_s(16).rjust(5, '0')}"
463
+ @cnt = 0
464
+ @tmp = 0
465
+ @state = :r7_status
466
+ end
467
+ @bits += @cmd.to_s
468
+ when :r7_status
469
+ if @cnt == 0 then
470
+ @times << @time
471
+ @phases << 'Status'
472
+ end
473
+ @tmp = (@tmp << 1) | @cmd
474
+ @cnt += 1
475
+ if @cnt == 14 then
476
+ str = "PCIe 1.2v: #{@tmp[13] == 1 ? 'Y' : 'N'}"
477
+ str += ", PCIe: #{@tmp[12] == 1 ? 'Y' : 'N'}"
478
+ str += ", Voltage accepted: #{{0=>'Not defined', 1=>'2.7-3.6V', 2=>'Res for LV range', 4=>'Res', 8=>'Res'}.fetch((@tmp >> 8) & 0x0f, 'Not defined')}"
479
+ str += ", Check pattern: #{@tmp & 0xff}"
480
+ puts " R7: #{str}"
481
+ @values << str
482
+ @cnt = 0
483
+ @crc = 0
484
+ @state = :r7_crc7
485
+ end
486
+ @bits += @cmd.to_s
487
+ when :r7_crc7
488
+ if @cnt == 0 then
489
+ @times << @time
490
+ @phases << 'CRC7'
491
+ end
492
+ @crc = (@crc << 1) | @cmd
493
+ @cnt += 1
494
+ if @cnt == 7 then
495
+ if @crc == crc7(@bits) then
496
+ @values << "#{@crc} (OK)"
497
+ else
498
+ puts "WARNING: CRC7(#{@bits}) = #{crc7(@bits)}, got, #{@crc}".light_red.bold
499
+ @values << "#{@crc} (FAIL -> #{crc7(@bits)})"
500
+ end
501
+ @state = :r_end_bit
502
+ end
503
+
504
+ ## R3 ##
505
+ when :r3_start_bit
506
+ if @cmd == 0 then
507
+ @times << @time
508
+ @phases << '?violet?Start bit'
509
+ @values << '?violet?0'
510
+ @bits = @cmd.to_s
511
+ @state = :r3_transmission_bit
512
+ end
513
+ when :r3_transmission_bit
514
+ @times << @time
515
+ @phases << 'Transmission bit'
516
+ @values << @cmd.to_s
517
+ @cnt = 0
518
+ @r_cmd_index = 0
519
+ @bits += @cmd.to_s
520
+ @state = :r3_command_index
521
+ puts 'WARNING: bit is 1 at response transmission bit'.light_red.bold if @cmd == 1
522
+ when :r3_command_index
523
+ if @cnt == 0 then
524
+ @times << @time
525
+ end
526
+ @r_cmd_index = (@r_cmd_index << 1) | @cmd
527
+ @cnt += 1
528
+ if @cnt == 6 then
529
+ @phases << 'Reserved'
530
+ @values << "0b#{@r_cmd_index.to_s(2).rjust(6,'0')}#{@r_cmd_index == 0b111111 ? '' : ' (ERROR)'}"
531
+ puts "WARNING: R3 reserved zone should be 0b111111 instead of 0b#{@r_cmd_index.to_s(2).rjust(6,'0')}".light_red.bold unless @r_cmd_index == 0b111111
532
+ @cnt = 0
533
+ @tmp = 0
534
+ @state = :r3_OCR
535
+ end
536
+ @bits += @cmd.to_s
537
+ when :r3_OCR
538
+ if @cnt == 0 then
539
+ @times << @time
540
+ @phases << 'OCR'
541
+ end
542
+ @tmp = (@tmp << 1) | @cmd
543
+ @cnt += 1
544
+ if @cnt == 32 then
545
+ puts " R3: OCR = 0x#{@tmp.to_s(16).rjust(8, '0')}"
546
+ @values << "OCR = 0x#{@tmp.to_s(16).rjust(8, '0')}"
547
+ @cnt = 0
548
+ @crc = 0
549
+ @state = :r3_crc7
550
+ end
551
+ @bits += @cmd.to_s
552
+ when :r3_crc7
553
+ if @cnt == 0 then
554
+ @times << @time
555
+ @phases << 'Reserved'
556
+ end
557
+ @crc = (@crc << 1) | @cmd
558
+ @cnt += 1
559
+ if @cnt == 7 then
560
+ @values << "0b#{@crc.to_s(2).rjust(7, '0')}"
561
+ puts "WARNING: R3 reserved zone should be 0b1111111 instead of 0b#{@crc.to_s(2).rjust(7,'0')}".light_red.bold unless @crc == 0b1111111
562
+ @state = :r_end_bit
563
+ end
564
+
565
+ ## R2 ##
566
+ when :r2_start_bit
567
+ if @cmd == 0 then
568
+ @times << @time
569
+ @phases << '?violet?Start bit'
570
+ @values << '?violet?0'
571
+ @state = :r2_transmission_bit
572
+ end
573
+ when :r2_transmission_bit
574
+ @times << @time
575
+ @phases << 'Transmission bit'
576
+ @values << @cmd.to_s
577
+ @cnt = 0
578
+ @r_cmd_index = 0
579
+ @state = :r2_command_index
580
+ puts 'WARNING: bit is 1 at response transmission bit'.light_red.bold if @cmd == 1
581
+ when :r2_command_index
582
+ if @cnt == 0 then
583
+ @times << @time
584
+ end
585
+ @r_cmd_index = (@r_cmd_index << 1) | @cmd
586
+ @cnt += 1
587
+ if @cnt == 6 then
588
+ @phases << 'Reserved'
589
+ @values << "0b#{@r_cmd_index.to_s(2).rjust(6,'0')}#{@r_cmd_index == 0b111111 ? '' : ' (ERROR)'}"
590
+ puts "WARNING: R2 reserved zone should be 0b111111 instead of 0b#{@r_cmd_index.to_s(2).rjust(6,'0')}".light_red.bold unless @r_cmd_index == 0b111111
591
+ @cnt = 0
592
+ @tmp = 0
593
+ @bits = ''
594
+ @state = :r2_CID_CSD
595
+ end
596
+ when :r2_CID_CSD
597
+ if @cnt == 0 then
598
+ @times << @time
599
+ @phases << (@cmd_index == 2 || @cmd_index == 10 ? 'CID' : 'CSD')
600
+ end
601
+ @tmp = (@tmp << 1) | @cmd
602
+ @cnt += 1
603
+ if @cnt == 120 then
604
+ if @cmd_index == 2 || @cmd_index == 10 then
605
+ puts " R2: CID = #{cid_to_s(@tmp)}"
606
+ @values << "CID = #{cid_to_s(@tmp)}"
607
+ else
608
+ puts " R2: CSD = 0x#{@tmp.to_s(16).rjust(30, '0')}"
609
+ @values << "CSD = 0x#{@tmp.to_s(16).rjust(30, '0')}"
610
+ end
611
+ @cnt = 0
612
+ @crc = 0
613
+ @state = :r_crc7
614
+ end
615
+ @bits += @cmd.to_s
616
+
617
+ ## R6 ##
618
+ when :r6_start_bit
619
+ if @cmd == 0 then
620
+ @times << @time
621
+ @phases << '?violet?Start bit'
622
+ @values << '?violet?0'
623
+ @bits = @cmd.to_s
624
+ @state = :r6_transmission_bit
625
+ end
626
+ when :r6_transmission_bit
627
+ @times << @time
628
+ @phases << 'Transmission bit'
629
+ @values << @cmd.to_s
630
+ @cnt = 0
631
+ @r_cmd_index = 0
632
+ @bits += @cmd.to_s
633
+ @state = :r6_command_index
634
+ puts 'WARNING: bit is 1 at response transmission bit'.light_red.bold if @cmd == 1
635
+ when :r6_command_index
636
+ if @cnt == 0 then
637
+ @times << @time
638
+ end
639
+ @r_cmd_index = (@r_cmd_index << 1) | @cmd
640
+ @cnt += 1
641
+ if @cnt == 6 then
642
+ @phases << 'CMD index'
643
+ if @r_cmd_index == @cmd_index then
644
+ @values << "#{@r_cmd_index} (OK)"
645
+ else
646
+ @values << "#{@r_cmd_index} (Should be #{@cmd_index})"
647
+ puts "WARNING: R1 response to command #{@r_cmd_index} instead of #{@cmd_index}".light_red.bold
648
+ end
649
+ @cnt = 0
650
+ @tmp = 0
651
+ @state = :r6_status
652
+ end
653
+ @bits += @cmd.to_s
654
+ when :r6_status
655
+ if @cnt == 0 then
656
+ @times << @time
657
+ @phases << 'Card status'
658
+ end
659
+ @tmp = (@tmp << 1) | @cmd
660
+ @cnt += 1
661
+ if @cnt == 32 then
662
+ cs = (@tmp & 0x1fff) | ((@tmp & 0x2000) << 6) | ((@tmp & 0x4000) << 8) | ((@tmp & 0x8000) << 8)
663
+ str = "RCA: #{(@tmp >> 16) & 0xffff}"
664
+ puts " R6: #{str}"
665
+ @values << str
666
+ @cnt = 0
667
+ @crc = 0
668
+ @state = :r_crc7
669
+ end
670
+ @bits += @cmd.to_s
671
+ end
672
+ end
673
+
674
+
675
+ def falling_edge
676
+ case @state
677
+ when :wait_host_start_bit
678
+ if @cmd == 0 then
679
+ @times << @time
680
+ @phases << '?green?Start bit'
681
+ @values << '?green?0'
682
+ @bits = @cmd.to_s
683
+ @state = :host_transmission_bit
684
+ end
685
+ when :host_transmission_bit
686
+ @times << @time
687
+ @phases << 'Transmission bit'
688
+ @values << @cmd.to_s
689
+ @cnt = 0
690
+ @cmd_index = 0
691
+ @bits += @cmd.to_s
692
+ @state = :host_command_index
693
+ puts 'WARNING: bit is 0 at host transmission bit'.light_red.bold if @cmd == 0
694
+ when :host_command_index
695
+ if @cnt == 0 then
696
+ @times << @time
697
+ end
698
+ @cmd_index = (@cmd_index << 1) | @cmd
699
+ @cnt += 1
700
+ if @cnt == 6 then
701
+ if @last_cmd_is_cmd_app then
702
+ @command = ACMDS[@cmd_index]
703
+ else
704
+ @command = CMDS[@cmd_index]
705
+ end
706
+ @last_cmd_is_cmd_app = (@cmd_index == 55)
707
+ @phases << @command[0]
708
+ @values << @command[2]
709
+ $stderr.write @command[2] ? @command[2].light_green.bold : @command[0].light_yellow.bold
710
+ @cnt = 0
711
+ @argument = 0
712
+ @state = :host_argument
713
+ end
714
+ @bits += @cmd.to_s
715
+ when :host_argument
716
+ if @cnt == 0 then
717
+ @times << @time
718
+ @phases << 'Argument'
719
+ end
720
+ @argument = (@argument << 1) | @cmd
721
+ @cnt += 1
722
+ if @cnt == 32 then
723
+ if @command[3].nil? then
724
+ @values << "0x#{@argument.to_s(16).rjust(8, '0')}"
725
+ puts
726
+ else
727
+ str = @command[3].collect{|range, name|
728
+ b = range.begin
729
+ e = range.end
730
+ e += 1 unless range.exclude_end?
731
+ l = e - b
732
+ v = (@argument >> b) & ((1 << l) - 1)
733
+ "#{name} = #{v}"
734
+ }.join(', ')
735
+ @values << str
736
+ puts ' ' + str
737
+ end
738
+ @cnt = 0
739
+ @crc = 0
740
+ @state = :host_crc7
741
+ end
742
+ @bits += @cmd.to_s
743
+ when :host_crc7
744
+ if @cnt == 0 then
745
+ @times << @time
746
+ @phases << 'CRC7'
747
+ end
748
+ @crc = (@crc << 1) | @cmd
749
+ @cnt += 1
750
+ if @cnt == 7 then
751
+ if @crc == crc7(@bits) then
752
+ @values << "#{@crc} (OK)"
753
+ else
754
+ puts "WARNING: CRC7(#{@bits}) = #{crc7(@bits)}, got, #{@crc}".light_red.bold
755
+ @values << "#{@crc} (FAIL -> #{crc7(@bits)})"
756
+ end
757
+ @state = :host_end_bit
758
+ end
759
+ when :host_end_bit
760
+ @times << @time
761
+ @phases << 'End bit'
762
+ @values << @cmd.to_s
763
+ @state = :command_send
764
+ puts 'WARNING: bit is 0 at end bit'.light_red.bold if @cmd == 0
765
+ when :command_send
766
+ @times << @time
767
+ @phases << nil
768
+ @values << nil
769
+ @state = case @command[1]
770
+ when :R1, :R1b
771
+ :r1_start_bit
772
+ when :R2
773
+ :r2_start_bit
774
+ when :R3
775
+ :r3_start_bit
776
+ when :R6
777
+ :r6_start_bit
778
+ when :R7
779
+ :r7_start_bit
780
+ when nil
781
+ :wait_host_start_bit
782
+ else
783
+ puts "Stopping because expecting unsupported #{@command[1]} response".light_yellow.bold
784
+ :wait
785
+ end
786
+ end
787
+ end
788
+
789
+ end
790
+
791
+
792
+ SDCardDecoder.run
793
+