10000 Add support for disassembling ESP32-S2 ULP binaries · pidou46/micropython-esp32-ulp@88803de · GitHub
[go: up one dir, main page]

Skip to content

Commit 88803de

Browse files
committed
Add support for disassembling ESP32-S2 ULP binaries
To disassemble ESP32-S2 ULP binaries use the `-c esp32s2` option when running the disassembler. Update documentation to mention support for the ESP32-S2.
1 parent b83b73d commit 88803de

10 files changed

+783
-12
lines changed

docs/disassembler.rst

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ You can also specify additional options to ``disassemble.py`` as follows:
2525
+--------------------------+----------------------------------------------------------------+
2626
| Option | Description |
2727
+==========================+================================================================+
28+
| ``-c`` or ``--mcpu`` | Choose ULP variant: either esp32 or esp32s2 |
29+
+--------------------------+----------------------------------------------------------------+
2830
| ``-h`` | Show help text |
2931
+--------------------------+----------------------------------------------------------------+
3032
|| ``-m <bytes sequence>`` || Disassemble a provided sequence of hex bytes |
@@ -43,18 +45,31 @@ specified file.
4345
Note that the ULP header is validates and files with unknown magic bytes will be
4446
rejected. The correct 4 magic bytes at the start of a ULP binary are ``ulp\x00``.
4547

46-
Example:
48+
Example disassembling an ESP32 ULP binary:
4749

4850
.. code-block:: shell
4951
5052
$ micropython -m tools.disassemble path/to/binary.ulp
5153
.text
5254
0000 040000d0 LD r0, r1, 0
53-
0004 0e0400d0 LD r2, r3, 1
55+
0004 0e0000d0 LD r2, r3, 0
56+
0008 04000068 ST r0, r1, 0
57+
000c 0b000068 ST r3, r2, 0
58+
.data
59+
0010 00000000 <empty>
60+
61+
Example disassembling an ESP32-S2 ULP binary:
62+
63+
.. code-block:: shell
64+
65+
$ micropython -m tools.disassemble -c esp32s2 path/to/binary.ulp
66+
.text
67+
0000 040000d0 LD r0, r1, 0
68+
0004 0e0000d0 LD r2, r3, 0
5469
0008 84010068 ST r0, r1, 0
55-
000c 8b090068 ST r3, r2, 2
70+
000c 8b010068 ST r3, r2, 0
5671
.data
57-
0000 00000000 <empty>
72+
0010 00000000 <empty>
5873
5974
6075
Disassembling a byte sequence
@@ -129,18 +144,20 @@ For example:
129144
Disassembling on device
130145
-----------------------------
131146

132-
The disassembler also works when used on an ESP32.
147+
The disassembler also works when used on an ESP32 device.
133148

134149
To use the disassembler on a real device:
135150

136151
* ensure ``micropython-esp32-ulp`` is installed on the device (see `docs/index.rst </docs/index.rst>`_).
137-
* upload ``tools/disassemble.py`` to the device (any directory will do)
138-
* run the following:
152+
* upload ``tools/disassemble.py`` ``tools/decode.py`` and ``tools/decode_s2.py`` to the device
153+
(any directory will do, as long as those 3 files are in the same directory)
154+
* the following example code assumes you placed the 3 files into the device's "root" directory
155+
* run the following (note, we must specify which the cpu the binary is for):
139156

140157
.. code-block:: python
141158
142159
from disassemble import disassemble_file
143160
# then either:
144-
disassemble_file('path/to/file.ulp') # normal mode
161+
disassemble_file('path/to/file.ulp', cpu='esp32s2') # normal mode
145162
# or:
146-
disassemble_file('path/to/file.ulp', True) # verbose mode
163+
disassemble_file('path/to/file.ulp', cpu='esp32s2', verbose=True) # verbose mode

tests/00_unit_tests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
set -e
66

7-
LIST=${1:-opcodes opcodes_s2 assemble link util preprocess definesdb decode}
7+
LIST=${1:-opcodes opcodes_s2 assemble link util preprocess definesdb decode decode_s2}
88

99
for file in $LIST; do
1010
echo testing $file...

tests/03_disassembler_tests.sh

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ test_disassembling_a_manual_sequence() {
4646
echo -e "Testing disassembling a manual byte sequence in NORMAL mode"
4747
fi
4848

49-
sequence="e1af 8c72 0100 0068 2705 cc19 0005 681d 0000 00a0 0000 0074"
49+
if [ "$cpu" == "esp32s2" ]; then
50+
sequence="e1af 8c74 8101 0068 2705 cc19 0005 681d 0000 00a0 0000 0078"
51+
else
52+
sequence="e1af 8c72 0100 0068 2705 cc19 0005 681d 0000 00a0 0000 0074"
53+
fi
5054

5155
lst_file="manual_bytes.$cpu.lst"
5256
lst_file_fixture=fixtures/manual_bytes${verbose}.$cpu.lst
@@ -69,3 +73,13 @@ test_disassembling_a_file esp32 verbose
6973

7074
test_disassembling_a_manual_sequence esp32
7175
test_disassembling_a_manual_sequence esp32 verbose
76+
77+
echo ""
78+
79+
# esp32s2
80+
echo "Testing for CPU: esp32s2"
81+
test_disassembling_a_file esp32s2
82+
test_disassembling_a_file esp32s2 verbose
83+
84+
test_disassembling_a_manual_sequence esp32s2
85+
test_disassembling_a_manual_sequence esp32s2 verbose

tests/decode_s2.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
from tools.decode_s2 import decode_instruction, get_instruction_fields
2+
import esp32_ulp.opcodes_s2 as opcodes
3+
import ubinascii
4+
5+
tests = []
6+
7+
8+
def test(param):
9+
tests.append(param)
10+
11+
12+
def hex_to_int(sequence):
13+
byte_sequence = ubinascii.unhexlify(sequence)
14+
return int.from_bytes(byte_sequence, 'little')
15+
16+
17+
def assert_decode(sequence, expected_struct, expected_name):
18+
i = hex_to_int(sequence)
19+
20+
ins, name = decode_instruction(i)
21+
22+
assert name == expected_name, '%s != %s' % (name, expected_name)
23+
assert ins is expected_struct, 'incorrect instruction struct (%s, %s)' % (sequence, name)
24+
25+
26+
def assert_decode_exception(sequence, expected_message):
27+
i = hex_to_int(sequence)
28+
29+
try:
30+
decode_instruction(i)
31+
except Exception as e:
32+
assert str(e) == expected_message, str(e)
33+
raised = True
34+
else:
35+
raised = False
36+
37+
assert raised, 'Exception not raised'
38+
39+
40+
def assert_decode_fields(sequence, expected_field_details):
41+
i = hex_to_int(sequence)
42+
43+
ins, _ = decode_instruction(i)
44+
45+
actual_field_details = get_instruction_fields(ins)
46+
47+
assert actual_field_details == expected_field_details, '\n- %s \n+ %s' % (actual_field_details, expected_field_details)
48+
49+
50+
@test
51+
def test_unknown_instruction():
52+
assert_decode_exception("10000001", 'Unknown instruction')
53+
54+
55+
@test
56+
def test_empty_instruction():
57+
assert_decode_exception("00000000", '<empty>')
58+
59+
60+
# All hex sequences were generated using our assembler.
61+
# Note: disassembled instructions always show field values according
62+
# to what is actually encoded into the binary instruction, not as per
63+
# original assembly code.
64+
# For example in JUMP instructions in the source code one would
65+
# specify jump offsets in bytes (e.g. 4 bytes) but in the actual
66+
# instruction offset encoded in the binary instruction will be in
67+
# words (1 word = 4 bytes).
68+
# The disassembled instructions would therefore show as "JUMP 1"
69+
# for what was originally "JUMP 4" in the source code.@test
70+
@test
71+
def test_all_instructions():
72+
# OPCODE_WR_REG = 1
73+
assert_decode("00000010", opcodes._wr_reg, 'REG_WR 0x0, 0, 0, 0')
74+
75+
# OPCODE_RD_REG = 2
76+
assert_decode("00000020", opcodes._rd_reg, 'REG_RD 0x0, 0, 0')
77+
78+
# OPCODE_I2C = 3
79+
assert_decode("00000030", opcodes._i2c, 'I2C_RD 0, 0, 0, 0')
80+
assert_decode("00000038", opcodes._i2c, 'I2C_WR 0, 0, 0, 0')
81+
82+
# OPCODE_DELAY = 4
83+
assert_decode("00000040", opcodes._delay, 'NOP')
84+
assert_decode("01000040", opcodes._delay, 'WAIT 1')
85+
86+
# OPCODE_ADC = 5
87+
assert_decode("00000050", opcodes._adc, 'ADC r0, 0, 0')
88+
89+
# OPCODE_ST = 6
90+
assert_decode("80010068", opcodes._st, 'ST r0, r0, 0')
91+
92+
# OPCODE_ALU = 7, SUB_OPCODE_ALU_REG
93+
assert_decode("00000070", opcodes._alu_reg, 'ADD r0, r0, r0')
94+
assert_decode("00002070", opcodes._alu_reg, 'SUB r0, r0, r0')
95+
assert_decode("00004070", opcodes._alu_reg, 'AND r0, r0, r0')
96+
assert_decode("00006070", opcodes._alu_reg, 'OR r0, r0, r0')
97+
assert_decode("00008070", opcodes._alu_reg, "MOVE r0, r0")
98+
assert_decode("0000a070", opcodes._alu_reg, 'LSH r0, r0, r0')
99+
assert_decode("0000c070", opcodes._alu_reg, 'RSH r0, r0, r0')
100+
101+
# OPCODE_ALU = 7, SUB_OPCODE_ALU_IMM
102+
assert_decode("00000074", opcodes._alu_imm, 'ADD r0, r0, 0')
103+
assert_decode("00002074", opcodes._alu_imm, 'SUB r0, r0, 0')
104+
assert_decode("00004074", opcodes._alu_imm, 'AND r0, r0, 0')
105+
assert_decode("00006074", opcodes._alu_imm, 'OR r0, r0, 0')
106+
assert_decode("00008074", opcodes._alu_imm, "MOVE r0, 0")
107+
assert_decode("0000a074", opcodes._alu_imm, 'LSH r0, r0, 0')
108+
assert_decode("0000c074", opcodes._alu_imm, 'RSH r0, r0, 0')
109+
110+
# OPCODE_ALU = 7, SUB_OPCODE_ALU_CNT
111+
assert_decode("00004078", opcodes._alu_cnt, 'STAGE_RST')
112+
assert_decode("00000078", opcodes._alu_cnt, 'STAGE_INC 0')
113+
assert_decode("00002078", opcodes._alu_cnt, 'STAGE_DEC 0')
114+
115+
# OPCODE_BRANCH = 8, SUB_OPCODE_BX (IMM)
116+
assert_decode("00000084", opcodes._bx, 'JUMP 0')
117+
assert_decode("00004084", opcodes._bx, 'JUMP 0, EQ')
118+
assert_decode("00008084", opcodes._bx, 'JUMP 0, OV')
119+
120+
# OPCODE_BRANCH = 8, SUB_OPCODE_BX (REG)
121+
assert_decode("00002084", opcodes._bx, 'JUMP r0')
122+
assert_decode("00006084", opcodes._bx, 'JUMP r0, EQ')
123+
assert_decode("0000a084", opcodes._bx, 'JUMP r0, OV')
124+
125+
# OPCODE_BRANCH = 8, SUB_OPCODE_BR
126+
assert_decode("00000080", opcodes._b, 'JUMPR 0, 0, LT')
127+
assert_decode("00000180", opcodes._b, 'JUMPR 0, 0, GT')
128+
assert_decode("00000280", opcodes._b, 'JUMPR 0, 0, EQ')
129+
130+
# OPCODE_BRANCH = 8, SUB_OPCODE_BX
131+
assert_decode("00800088", opcodes._bs, 'JUMPS 0, 0, LT')
132+
assert_decode("00800188", opcodes._bs, 'JUMPS 0, 0, GT')
133+
assert_decode("00000288", opcodes._bs, 'JUMPS 0, 0, EQ')
134+
assert_decode("00800288", opcodes._bs, 'JUMPS 0, 0, LE')
135+
assert_decode("00800388", opcodes._bs, 'JUMPS 0, 0, GE')
136+
137+
# OPCODE_END = 9, SUB_OPCODE_END
138+
assert_decode("01000090", opcodes._end, 'WAKE')
139+
140+
# OPCODE_END = 9, SUB_OPCODE_SLEEP
141+
###assert_decode("01000040", opcodes._end, 'SLEEP 1') ##TODO
142+
143+
# OPCODE_TSENS = 10
144+
assert_decode("000000a0", opcodes._tsens, 'TSENS r0, 0')
145+
146+
# OPCODE_HALT = 11
147+
assert_decode("000000b0", opcodes._halt, 'HALT')
148+
149+
# OPCODE_LD = 13
150+
assert_decode("000000d0", opcodes._ld, 'LD r0, r0, 0')
151+
152+
153+
if __name__ == '__main__':
154+
# run all methods marked with @test
155+
for t in tests:
156+
t()

0 commit comments

Comments
 (0)
0