Skip to content

Commit 4e748c8

Browse files
committed
tools/test_serial.py: Add test for serial throughput.
Signed-off-by: Damien George <damien@micropython.org>
1 parent 51974f2 commit 4e748c8

File tree

1 file changed

+235
-0
lines changed

1 file changed

+235
-0
lines changed

tools/test_serial.py

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
#!/usr/bin/env python
2+
#
3+
# Performance and reliability test for serial port communication.
4+
#
5+
# Usage:
6+
# test_serial.py [cdc-device]
7+
#
8+
# The `cdc-device` will default to /dev/ttyACM0.
9+
10+
import sys
11+
import time
12+
import argparse
13+
import serial
14+
15+
def drain_input(ser):
16+
time.sleep(0.1)
17+
while ser.inWaiting() > 0:
18+
data = ser.read(ser.inWaiting())
19+
time.sleep(0.1)
20+
21+
read_test_script = """
22+
vcp_id = %u
23+
led = None
24+
try:
25+
import pyb
26+
pyb.LED(1).on()
27+
led = pyb.LED(2)
28+
assert pyb.USB_VCP(vcp_id).isconnected()
29+
wr=pyb.USB_VCP(vcp_id).send
30+
except:
31+
import sys
32+
wr=sys.stdout.buffer.write
33+
b=bytearray(%u)
34+
for i in range(len(b)):
35+
b[i] = i & 0xff
36+
for _ in range(%d):
37+
if led:
38+
led.toggle()
39+
n = wr(b)
40+
"""
41+
42+
def read_test(ser_repl, ser_data, usb_vcp_id, bufsize, nbuf):
43+
assert bufsize % 256 == 0 # for verify to work
44+
45+
# Load and run the read_test_script.
46+
ser_repl.write(b'\x03\x03\x01\x04') # break, break, raw-repl, soft-reboot
47+
drain_input(ser_repl)
48+
ser_repl.write(bytes(read_test_script % (usb_vcp_id, bufsize, nbuf), 'ascii'))
49+
ser_repl.write(b'\x04') # eof
50+
ser_repl.flush()
51+
response = ser_repl.read(2)
52+
assert response == b"OK", response
53+
54+
# Read data from the device, check it is correct, and measure throughput.
55+
n = 0
56+
last_byte = None
57+
t_start = time.time()
58+
remain = nbuf * bufsize
59+
total_data = bytearray(remain)
60+
while remain:
61+
t0 = time.monotonic_ns()
62+
while ser_data.inWaiting() == 0:
63+
if time.monotonic_ns() - t0 > 1e9:
64+
# timeout waiting for data from device
65+
break
66+
time.sleep(0.0001)
67+
if not ser_data.inWaiting():
68+
print('ERROR: timeout waiting for data')
69+
return 0
70+
to_read = min(ser_data.inWaiting(), remain)
71+
data = ser_data.read(to_read)
72+
# verify bytes coming in are in sequence
73+
#if last_byte is not None:
74+
# if data[0] != (last_byte + 1) & 0xff:
75+
# print('ERROR: first byte is not in sequence:', last_byte, data[0])
76+
#last_byte = data[-1]
77+
#for i in range(1, len(data)):
78+
# if data[i] != (data[i - 1] + 1) & 0xff:
79+
# print('ERROR: data not in sequence at position %d:' % i, data[i - 1], data[i])
80+
remain -= len(data)
81+
print(n, nbuf * bufsize, end="\r")
82+
total_data[n:n+len(data)] = data
83+
n += len(data)
84+
t_end = time.time()
85+
for i in range(0, len(total_data)):
86+
if total_data[i] != i & 0xff:
87+
print("fail", i, i&0xff, total_data[i])
88+
ser_repl.write(b'\x03') # break
89+
t = t_end - t_start
90+
91+
# Print results.
92+
print("DATA IN: bufsize=%u, read %u bytes in %.2f msec = %.2f kibytes/sec = %.2f MBits/sec" % (bufsize, n, t * 1000, n / 1024 / t, n * 8 / 1000000 / t))
93+
94+
return t
95+
96+
write_test_script_verified = """
97+
import sys
98+
vcp_id = %u
99+
led=None
100+
try:
101+
import pyb
102+
pyb.LED(1).on()
103+
led=pyb.LED(2)
104+
assert pyb.USB_VCP(vcp_id).isconnected()
105+
rd=pyb.USB_VCP(vcp_id).recv
106+
except:
107+
rd=sys.stdin.readinto
108+
b=bytearray(%u)
109+
while 1:
110+
if led:
111+
led.toggle()
112+
n = rd(b)
113+
fail = 0
114+
for i in range(n):
115+
if b[i] != 32 + (i & 0x3f):
116+
fail += 1
117+
if fail:
118+
sys.stdout.write(b'ER%%04u' %% fail)
119+
else:
120+
sys.stdout.write(b'OK%%04u' %% n)
121+
"""
122+
123+
write_test_script_unverified = """
124+
import sys
125+
vcp_id = %u
126+
led=None
127+
try:
128+
import pyb
129+
pyb.LED(1).on()
130+
led=pyb.LED(2)
131+
assert pyb.USB_VCP(vcp_id).isconnected()
132+
rd=pyb.USB_VCP(vcp_id).recv
133+
except:
134+
rd=sys.stdin.readinto
135+
b=bytearray(%u)
136+
while 1:
137+
if led:
138+
led.toggle()
139+
n = rd(b)
140+
if n != len(b):
141+
sys.stdout.write(b'ER%%04u' %% n)
142+
else:
143+
sys.stdout.write(b'OK%%04u' %% n)
144+
"""
145+
146+
def write_test(ser_repl, ser_data, usb_vcp_id, bufsize, nbuf, verified):
147+
# Load and run the write_test_script.
148+
#ser_repl.write(b'\x03\x03\x01\x04') # break, break, raw-repl, soft-reboot
149+
ser_repl.write(b'\x03\x01\x04') # break, raw-repl, soft-reboot
150+
drain_input(ser_repl)
151+
if verified:
152+
script = write_test_script_verified
153+
else:
154+
script = write_test_script_unverified
155+
ser_repl.write(bytes(script % (usb_vcp_id, bufsize), 'ascii'))
156+
ser_repl.write(b'\x04') # eof
157+
ser_repl.flush()
158+
drain_input(ser_repl)
159+
160+
# Write data to the device, check it is correct, and measure throughput.
161+
n = 0
162+
t_start = time.time()
163+
buf = bytearray(bufsize)
164+
for i in range(len(buf)):
165+
buf[i] = 32 + (i & 0x3f) # don't want to send ctrl chars!
166+
for i in range(nbuf):
167+
ser_data.write(buf)
168+
n += len(buf)
169+
#while ser_data.inWaiting() == 0:
170+
# time.sleep(0.001)
171+
#response = ser_data.read(ser_data.inWaiting())
172+
response = ser_repl.read(6)
173+
if response != b'OK%04u' % bufsize:
174+
print('bad response, expecting OK%04u, got %r' % (bufsize, response))
175+
t_end = time.time()
176+
ser_repl.write(b'\x03') # break
177+
t = t_end - t_start
178+
179+
# Print results.
180+
print("DATA OUT: verify=%d, bufsize=%u, wrote %u bytes in %.2f msec = %.2f kibytes/sec = %.2f MBits/sec" % (verified, bufsize, n, t * 1000, n / 1024 / t, n * 8 / 1000000 / t))
181+
182+
return t
183+
184+
def main():
185+
dev_repl = '/dev/ttyACM0'
186+
dev_data = None
187+
if len(sys.argv) >= 2:
188+
dev_repl = sys.argv[1]
189+
if len(sys.argv) >= 3:
190+
assert len(sys.argv) >= 4
191+
dev_data = sys.argv[2]
192+
usb_vcp_id = int(sys.argv[3])
193+
194+
if dev_data is None:
195+
print('REPL and data on', dev_repl)
196+
ser_repl = serial.Serial(dev_repl, baudrate=115200)
197+
ser_data = ser_repl
198+
usb_vcp_id = 0
199+
else:
200+
print('REPL on', dev_repl)
201+
print('data on', dev_data)
202+
print('USB VCP', usb_vcp_id)
203+
ser_repl = serial.Serial(dev_repl, baudrate=115200)
204+
ser_data = serial.Serial(dev_data, baudrate=115200)
205+
206+
if 0:
207+
for i in range(1000):
208+
print('======== TEST %04u ========' % i)
209+
read_test(ser_repl, ser_data, usb_vcp_id, 8000, 32)
210+
write_test(ser_repl, ser_data, usb_vcp_id, 8000, 32, True)
211+
return
212+
213+
read_test_params = [(256, 128), (512, 64), (1024, 64), (2048, 64), (4096, 64), (8192, 64), (16384, 64)]
214+
write_test_params = [(128, 32), (256, 16), (512, 16), (1024, 16), (2048, 16), (4096, 16), (8192, 32), (9999, 64)]
215+
216+
for bufsize, nbuf in read_test_params:
217+
t = read_test(ser_repl, ser_data, usb_vcp_id, bufsize, nbuf)
218+
if t > 8:
219+
break
220+
221+
for bufsize, nbuf in write_test_params:
222+
t = write_test(ser_repl, ser_data, usb_vcp_id, bufsize, nbuf, True)
223+
if t > 8:
224+
break
225+
226+
for bufsize, nbuf in write_test_params:
227+
t = write_test(ser_repl, ser_data, usb_vcp_id, bufsize, nbuf, False)
228+
if t > 8:
229+
break
230+
231+
ser_repl.close()
232+
ser_data.close()
233+
234+
if __name__ == "__main__":
235+
main()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy