-
Notifications
You must be signed in to change notification settings - Fork 2
/
teensy_rpc.py
189 lines (173 loc) · 9.37 KB
/
teensy_rpc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# bare-bones rpc client implementation for teensy4vfi
import sys, struct
import serial
DEFAULT_PORT = 'COM3' # teensy
DEFAULT_BAUD = 115200 # default teensy debug uart baud
RPC_MAGIC = b'@' # byte indicating we are talking to teensy
RPC_WATERMARK = b'!PC_' # prefix indicating teensy is replying to us
RPC_COMMANDS = { # supported RPC commands & short descriptions
"ping" : [0x0, "get rpc server firmware build timestamp", ""],
"read32" : [0x1, "read 32bits from a memory address", "[address]"],
"write32" : [0x2, "write 32bits to a memory address", "[address] [data]"],
"memset" : [0x3, "fill a memory range (8bit)", "[start] [fill] [size]"],
"memcpy" : [0x4, "copy a memory range to another address", "[dst] [src] [size]"],
"delay" : [0x5, "set rpc server reply delay", "[delay]"],
"stop" : [0x6, "stop the rpc server", ""],
"hexdump" : [0x7, "print a memory range (hex)", "[address] [size] <print line addr?>"],
"memset32" : [0x8, "fill a memory range (32bit)", "[start] [fill] [size]"],
"glitch_prep_ll" : [0x9, "prepare a default logic level -triggered glitch", "<offset> <offset_mult> <width> <add to chain?> <trigger pad> <trigger state> <driver pad>"],
"glitch_arm" : [0xa, "execute the glitch chain", "<fake rpc reply to free the client?>"],
"glitch_prep_custom" : [0xb, "prepare a custom glitch", "[glitch_config]"],
"glitch_prep_uart" : [0xc, "prepare a default uart -triggered glitch", "<offset> <offset_mult> <width> <add to chain?> <trigger pad> <trigger char> <driver pad>"],
"set_clk" : [0xd, "set core freq", "[freq] <clkf>"],
"glitch_prep_custom_chain" : [0xe, "prepare a custom glitch (add to chain)", "[glitch_config]"],
"glitch_prep_none" : [0xf, "prepare a default glitch (no trigger)", "<offset> <offset_mult> <width> <add to chain?> <driver pad>"],
"custom" : [0x10, "run the custom_main func", "[arg0] [arg1] [arg2] <data>"],
"uart_init" : [0x11, "init a teensy uartn", "[uartn] [baud] [flags] <wait?>"], #TODO: wrap
"pad_configure" : [0x12, "set pad_ctl and mux_ctl for a teensy pad" "[pad] [pad_ctl] [mux_ctl]"], #TODO: wrap
"pad_ctrl_logic" : [0x13, "control pad state (logic level mode)", "[func] [pad] <wait?>"], #TODO: wrap
"glitch_set_chain_max" : [0x14, "set glitch varray/chain location and max elements", "<max glitches> <in heap?>"],
"get_sp" : [0x15, "get the current stack pointer", ""],
"glitch_arm_loop" : [0x16, "execute the glitch chain in an infinite loop", ""]
}
DEFAULT_DRIVER_PAD = 22 # aka mosfet pad for VFI
DEFAULT_LL_TRIGGER_PAD = 23 # logic-level trigger pad
DEFAULT_UART_TRIGGER_N = 1 # uart trigger teensy uartn
DEFAULT_UART_TRIGGER_EXP_BYTE = b'\xD9' # uart trigger byte
DEFAULT_ARG_DICT = { # our glitch_add vars with default preset, translated into glitch_prep_* args
"offset" : [1, "how many ~cycles from trigger to glitch"],
"offset_mult" : [1, "how many times to repeat the offset loop"],
"width" : [1, "how many ~cycles the glitch is held"],
"trigger" : [23, "trigger pad or uartn"],
"trigger_state" : [0, "trigger logic state or uart byte"],
"driver" : [22, "(mosfet) glitch driver pad"],
"queue" : [0, "should this glitch be added to the chain?"],
"no_trigger" : [0, "skip the glitch trigger"],
"no_driver" : [0, "skip the glitch driver"],
"uart_mode" : [0, "set this glitch to use an uart trigger"],
"override" : [0, "set this to enable default config overrides (O)"],
"clockspeed" : [0, "(O) glitch clockspeed / core frequency"],
"driver_mask" : [0, "(O) glitch driver start/stop pattern"],
"driver_set_drive" : [0, "(O) glitch driver start register addr"],
"driver_set_stop" : [0, "(O) glitch driver stop register addr"],
"trigger_mask" : [0, "(O) trigger data pattern mask"], # MUST SET trigger_exp TOO
"trigger_exp" : [0, "(O) trigger expected data pattern"],
"trigger_get" : [0, "(O) trigger data pattern register addr"],
"driver_reconfigure" : [0, "(O) set this to reconfigure the driver pad (D)"],
"driver_ode" : [0, "(O) (D) enable open-drain mode"],
"driver_dse" : [7, "(O) (D) set drive strength divider"],
"trigger_reconfigure" : [0, "(O) set this to reconfigure the trigger pad (T)"],
"trigger_pke" : [0, "(O) (T) enable pull/keep"],
"trigger_pue" : [0, "(O) (T) set pull mode"],
"trigger_pus" : [0, "(O) (T) set pull type"],
"trigger_hys" : [0, "(O) (T) enable hysteresis"],
}
uart = serial.Serial(DEFAULT_PORT, baudrate=DEFAULT_BAUD, timeout=1)
def send_rpc_cmd(id, argv):
data = bytearray()
for arg in argv:
data.extend(struct.pack('<I', arg))
headr = bytearray([int.from_bytes(RPC_MAGIC, "little"), RPC_COMMANDS[id][0], len(data), (RPC_COMMANDS[id][0] + len(data))])
#print(headr.hex().upper())
#print(data.hex().upper())
uart.reset_output_buffer()
uart.reset_input_buffer()
uart.write(headr)
cont = uart.readline()
while not RPC_WATERMARK in cont:
cont = uart.readline()
if cont == RPC_WATERMARK + b'G1\r\n':
uart.write(data)
cont = uart.readline()
while not RPC_WATERMARK in cont:
cont = uart.readline()
cont = cont.decode('utf-8').strip("!PC_")
print(cont)
def glitch_add(argd):
#print(argd)
cmd = "glitch_prep_ll"
if argd["uart_mode"] == 1:
cmd = "glitch_prep_uart"
flags = argd["queue"] | (argd["no_driver"] << 1) | (argd["no_trigger"] << 2)
argv = [argd["offset"], argd["offset_mult"], argd["width"], flags, argd["trigger"], argd["trigger_state"], argd["driver"]]
#print(argv)
send_rpc_cmd(cmd, argv)
def glitch_add_direct(argd):
#print(argd)
cmd = "glitch_prep_custom"
if argd["queue"] == 1:
cmd = "glitch_prep_custom_chain"
#TODO: maybe handle it in a nice dictionary/array where idx = bit shift?
trigger_ctl = argd["trigger"] | (argd["no_trigger"] << 6) | (argd["uart_mode"] << 8) | (argd["trigger_state"] << 16)
if argd["trigger_reconfigure"] == 1:
trigger_ctl = trigger_ctl | (1 << 7) | (argd["trigger_pke"] << 9) | (argd["trigger_pue"] << 10) | (argd["trigger_pus"] << 11) | (argd["trigger_hys"] << 13)
driver_ctl = argd["driver"] | (argd["no_driver"] << 6)
if argd["driver_reconfigure"] == 1:
driver_ctl = driver_ctl | (1 << 7) | (argd["driver_ode"] << 8) | (argd["driver_dse"] << 9)
argv = [argd["width"], argd["offset"], argd["offset_mult"], trigger_ctl, driver_ctl, argd["clockspeed"], argd["driver_mask"], argd["driver_set_drive"], argd["driver_set_stop"], argd["trigger_mask"], argd["trigger_exp"], argd["trigger_get"]]
#print(argv)
send_rpc_cmd(cmd, argv)
# "ui design is my passion" xD
def helper(focus):
match focus:
case "none":
print(f"{'CMD':>24}" + " : " + "DESCRIPTION")
print(f"{'---':>24}" + " : " + "-----------")
for cmd in RPC_COMMANDS:
print(f"{cmd:>24}" + " : " + RPC_COMMANDS[cmd][1])
print(f"{' ':>24}" + " ! " + " ")
print(f"{'glitch_add':>24}" + " : " + "wrapper for glitch_prep_* funcs")
print(f"{'manual':>24}" + " : " + "manual trigger for default glitch_prep_uart")
print(f"{'help <CMD>':>24}" + " : " + "show me or CMD's expected args")
case "glitch_add":
print("\nUsage: " + focus + " param=value par6=val6 par3=val3 ...\n")
print("Descr: " + "prepare a glitch with specified params" + "\n")
print(f"{'PARAM':>20}" + " : " + f"{'DEFAULT':^7}" + " : " + "DESCRIPTION")
print(f"{'-----':>20}" + " : " + f"{'-------':^7}" + " : " + "-----------")
for arg in DEFAULT_ARG_DICT:
print(f"{arg:>20}" + " : " + f"{str(DEFAULT_ARG_DICT[arg][0]):^7}" + " : " + DEFAULT_ARG_DICT[arg][1])
case _:
if focus in RPC_COMMANDS:
print("\nUsage: " + focus + " " + RPC_COMMANDS[focus][2] + "\n")
print("Descr: " + RPC_COMMANDS[focus][1] + "\n")
else:
print("command not found and/or malformed input")
return ""
def handle_cmd(cmd, argv):
match cmd:
case "manual":
return uart.write(DEFAULT_UART_TRIGGER_EXP_BYTE)
case "glitch_add":
arg_dict = {param: value[0] for param, value in DEFAULT_ARG_DICT.copy().items()}
for arg in argv:
key, val = arg.split('=')
if val.startswith('0x'):
arg_dict[key] = int(val, 16)
else:
arg_dict[key] = int(val)
if arg_dict["override"] == 1:
return glitch_add_direct(arg_dict)
else:
return glitch_add(arg_dict)
case "help":
if len(argv) > 0:
return helper(argv[0])
return helper("none")
case _:
if cmd in RPC_COMMANDS:
rargv = []
for arg in argv:
if arg.startswith('0x'):
rargv.append(int(arg, 16))
else:
rargv.append(int(arg))
return send_rpc_cmd(cmd, rargv)
else:
print("command not found and/or malformed input")
return ""
if __name__ == "__main__":
if len(sys.argv) > 1:
handle_cmd(sys.argv[1], sys.argv[2:])
else:
helper("none")
uart.close()