aboutsummaryrefslogtreecommitdiff
path: root/fw/tools
diff options
context:
space:
mode:
authorjaseg <git@jaseg.net>2018-01-05 11:53:52 +0100
committerjaseg <git@jaseg.net>2018-01-05 11:53:52 +0100
commite3094b18d4fc1b5f8e5e7ca146318e501a7a4ff9 (patch)
treee70e8d966a82d84f476b9d05b3b358678f38b42f /fw/tools
parent31fc78d0e00f9c82f03c88367a7e23b2df918ca6 (diff)
download7seg-e3094b18d4fc1b5f8e5e7ca146318e501a7a4ff9.tar.gz
7seg-e3094b18d4fc1b5f8e5e7ca146318e501a7a4ff9.tar.bz2
7seg-e3094b18d4fc1b5f8e5e7ca146318e501a7a4ff9.zip
Firmware directory reorganization
Diffstat (limited to 'fw/tools')
-rw-r--r--fw/tools/MatrixTransform.ipynb218
-rw-r--r--fw/tools/circle.jpgbin0 -> 17331 bytes
-rw-r--r--fw/tools/gen_cmsis_exports.py30
-rw-r--r--fw/tools/mapparse.py129
-rw-r--r--fw/tools/mapvis.py25
-rw-r--r--fw/tools/measure_transpose_performance.gdb17
-rw-r--r--fw/tools/profile.gdb5
-rw-r--r--fw/tools/profile.sh26
m---------fw/tools/pymapfile0
-rw-r--r--fw/tools/scans/digit.pngbin0 -> 5043 bytes
-rw-r--r--fw/tools/scans/megumin_segmentmap_600dpi.jpgbin0 -> 1786337 bytes
-rw-r--r--fw/tools/scans/pretty.jpgbin0 -> 136795 bytes
-rw-r--r--fw/tools/scans/pretty.pngbin0 -> 157263 bytes
-rw-r--r--fw/tools/scans/pretty2.pngbin0 -> 270647 bytes
-rw-r--r--fw/tools/segmasks/seg1.pngbin0 -> 859 bytes
-rw-r--r--fw/tools/segmasks/seg2.pngbin0 -> 1406 bytes
-rw-r--r--fw/tools/segmasks/seg3.pngbin0 -> 1355 bytes
-rw-r--r--fw/tools/segmasks/seg4.pngbin0 -> 914 bytes
-rw-r--r--fw/tools/segmasks/seg5.pngbin0 -> 1360 bytes
-rw-r--r--fw/tools/segmasks/seg6.pngbin0 -> 1340 bytes
-rw-r--r--fw/tools/segmasks/seg7.pngbin0 -> 781 bytes
-rw-r--r--fw/tools/segmasks/seg8.pngbin0 -> 699 bytes
-rwxr-xr-xfw/tools/test.py140
-rw-r--r--fw/tools/transpose_test.c77
24 files changed, 667 insertions, 0 deletions
diff --git a/fw/tools/MatrixTransform.ipynb b/fw/tools/MatrixTransform.ipynb
new file mode 100644
index 0000000..8e95469
--- /dev/null
+++ b/fw/tools/MatrixTransform.ipynb
@@ -0,0 +1,218 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 129,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "import struct\n",
+ "\n",
+ "import serial\n",
+ "import PIL\n",
+ "import numpy as np\n",
+ "from matplotlib import pyplot as plt\n",
+ "%matplotlib inline\n",
+ "\n",
+ "import crc"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 102,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "digit_mask = PIL.Image.open('scans/digit.png')\n",
+ "seg_masks = [ np.asarray(PIL.Image.open('segmasks/seg{}.png'.format(i)), dtype=np.float)[:,:,0]/255.0 for i in range(1,9) ]\n",
+ "digit_h, digit_w = seg_masks[0].shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 193,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "pic = PIL.Image.open('circle.jpg')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 194,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "dpi = 600\n",
+ "scale = 1000/5091\n",
+ "stride_x, stride_y = 1.0*dpi*scale, 38.8/25.4*dpi*scale"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 195,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "mask_w, mask_h = int(8*stride_x)+1, int(4*stride_y)+1\n",
+ "resized = np.asarray(pic.resize((mask_w, mask_h), PIL.Image.BILINEAR).convert('HSV'), dtype=np.float)[:,:,2]/255"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 196,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "def sub(resized, x, y):\n",
+ " return resized[int(y*stride_y):int(y*stride_y)+digit_h, int(x*stride_x):int(x*stride_x)+digit_w]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 197,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "def digit_values(resized, x, y):\n",
+ " img = sub(resized, x, y)/255.0\n",
+ " for mask in seg_masks:\n",
+ " yield np.average(np.multiply(mask, img))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 198,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "def make_vals(resized):\n",
+ " subvals = np.zeros((8, 4, 8), dtype=np.float)\n",
+ " for x in range(8):\n",
+ " for y in range(4):\n",
+ " subvals[x,y] = list(digit_values(resized, x, y))\n",
+ " return subvals"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 204,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "def send_packet(ser, resized, addr=5):\n",
+ " gamma = 2\n",
+ " out = np.clip(np.power(make_vals(resized), 1/gamma)*255, 0, 255).astype(np.uint8)\n",
+ " pkt = b''\n",
+ " for x in range(8):\n",
+ " for y in range(4):\n",
+ " pkt += bytes(out[x,y,:]) + b'\\x00\\x00'\n",
+ " pkt += b'\\x03\\x00\\x00\\x00'\n",
+ " pkt += struct.pack('I', crc.crc(pkt))\n",
+ " pkt = bytes([0x40 | addr]) + pkt\n",
+ " ser.write(pkt)\n",
+ " return out"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 205,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([[[3, 3, 3, 3, 3, 3, 3, 1],\n",
+ " [3, 3, 3, 3, 3, 3, 3, 1],\n",
+ " [3, 3, 3, 3, 3, 3, 3, 1],\n",
+ " [3, 3, 3, 3, 3, 3, 3, 1]],\n",
+ "\n",
+ " [[3, 3, 3, 3, 3, 3, 3, 1],\n",
+ " [3, 3, 3, 3, 3, 3, 3, 0],\n",
+ " [2, 3, 0, 3, 3, 3, 3, 1],\n",
+ " [3, 3, 3, 3, 3, 3, 3, 1]],\n",
+ "\n",
+ " [[3, 3, 3, 3, 3, 3, 3, 1],\n",
+ " [1, 2, 0, 0, 0, 0, 0, 0],\n",
+ " [0, 0, 0, 0, 0, 0, 0, 0],\n",
+ " [3, 3, 3, 3, 3, 3, 3, 1]],\n",
+ "\n",
+ " [[3, 3, 3, 3, 3, 3, 3, 1],\n",
+ " [0, 0, 0, 0, 0, 0, 0, 0],\n",
+ " [0, 0, 0, 0, 0, 0, 0, 0],\n",
+ " [0, 2, 2, 3, 3, 3, 3, 1]],\n",
+ "\n",
+ " [[3, 3, 3, 3, 3, 3, 3, 1],\n",
+ " [0, 0, 0, 0, 0, 0, 0, 0],\n",
+ " [0, 0, 0, 0, 0, 0, 0, 0],\n",
+ " [0, 2, 2, 3, 3, 3, 3, 1]],\n",
+ "\n",
+ " [[3, 3, 3, 3, 3, 3, 3, 1],\n",
+ " [1, 0, 2, 0, 0, 0, 0, 0],\n",
+ " [0, 0, 0, 0, 0, 0, 0, 0],\n",
+ " [3, 3, 3, 3, 3, 3, 3, 1]],\n",
+ "\n",
+ " [[3, 3, 3, 3, 3, 3, 3, 1],\n",
+ " [3, 3, 3, 3, 0, 3, 1, 1],\n",
+ " [2, 0, 3, 2, 1, 3, 3, 1],\n",
+ " [3, 3, 3, 3, 3, 3, 3, 1]],\n",
+ "\n",
+ " [[3, 3, 3, 3, 3, 3, 3, 1],\n",
+ " [3, 3, 3, 3, 3, 3, 3, 1],\n",
+ " [3, 3, 3, 3, 3, 3, 3, 1],\n",
+ " [3, 3, 3, 3, 3, 3, 3, 1]]], dtype=uint8)"
+ ]
+ },
+ "execution_count": 205,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "ser = serial.Serial('/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A90JRHXH-if00-port0', 2000000)\n",
+ "send_packet(ser, resized)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.5.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/fw/tools/circle.jpg b/fw/tools/circle.jpg
new file mode 100644
index 0000000..afb6e3e
--- /dev/null
+++ b/fw/tools/circle.jpg
Binary files differ
diff --git a/fw/tools/gen_cmsis_exports.py b/fw/tools/gen_cmsis_exports.py
new file mode 100644
index 0000000..ba3422b
--- /dev/null
+++ b/fw/tools/gen_cmsis_exports.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+import re
+import os
+
+if __name__ == '__main__':
+ import argparse
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('cmsis_device_header', nargs='+', type=argparse.FileType('rb'))
+ args = parser.parse_args()
+
+ print('#ifndef __GENERATED_CMSIS_HEADER_EXPORTS__')
+ print('#define __GENERATED_CMSIS_HEADER_EXPORTS__')
+ print()
+ for header in args.cmsis_device_header:
+ lines = header.readlines()
+ name = os.path.basename(header.name)
+ print('#include <{}>'.format(name))
+ print()
+
+ print('/* {} */'.format(name))
+ for l in lines:
+ match = re.match(b'^#define (\w+)\s+\W*(\w+_TypeDef|\w+_Type).*$', l)
+ if match:
+ inst, typedef = match.groups()
+ inst, typedef = inst.decode(), typedef.decode()
+ print('{} *{} = {};'.format(typedef, inst.lower(), inst))
+ print()
+ print('#endif//__GENERATED_CMSIS_HEADER_EXPORTS__')
+
diff --git a/fw/tools/mapparse.py b/fw/tools/mapparse.py
new file mode 100644
index 0000000..c1f460a
--- /dev/null
+++ b/fw/tools/mapparse.py
@@ -0,0 +1,129 @@
+
+import re
+from collections import defaultdict, namedtuple
+
+Section = namedtuple('Section', ['name', 'offset', 'objects'])
+ObjectEntry = namedtuple('ObjectEntry', ['filename', 'object', 'offset', 'size'])
+FileEntry = namedtuple('FileEntry', ['section', 'object', 'offset', 'length'])
+
+class Memory:
+ def __init__(self, name, origin, length, attrs=''):
+ self.name, self.origin, self.length, self.attrs = name, origin, length, attrs
+ self.sections = {}
+ self.files = defaultdict(lambda: [])
+ self.totals = defaultdict(lambda: 0)
+
+ def add_toplevel(self, name, offx, length):
+ self.sections[name] = Section(offx, length, [])
+
+ def add_obj(self, name, offx, length, fn, obj):
+ base_section, sep, subsec = name[1:].partition('.')
+ base_section = '.'+base_section
+ if base_section in self.sections:
+ sec = secname, secoffx, secobjs = self.sections[base_section]
+ secobjs.append(ObjectEntry(fn, obj, offx, length))
+ else:
+ sec = None
+ self.files[fn].append(FileEntry(sec, obj, offx, length))
+ self.totals[fn] += length
+
+class MapFile:
+ def __init__(self, s):
+ self._lines = s.splitlines()
+ self.memcfg = {}
+ self.defaultmem = Memory('default', 0, 0xffffffffffffffff)
+ self._parse()
+
+ def __getitem__(self, offx_or_name):
+ ''' Lookup a memory area by name or address '''
+ if offx_or_name in self.memcfg:
+ return self.memcfg[offx_or_name]
+
+ elif isinstance(offx_or_name, int):
+ for mem in self.memcfg.values():
+ if mem.origin <= offx_or_name < mem.origin+mem.length:
+ return mem
+ else:
+ return self.defaultmem
+
+ raise ValueError('Invalid argument type for indexing')
+
+ def _skip(self, regex):
+ matcher = re.compile(regex)
+ for l in self:
+ if matcher.match(l):
+ break
+
+ def __iter__(self):
+ while self._lines:
+ yield self._lines.pop(0)
+
+ def _parse(self):
+ self._skip('^Memory Configuration')
+
+ # Parse memory segmentation info
+ self._skip('^Name')
+ for l in self:
+ if not l:
+ break
+ name, origin, length, *attrs = l.split()
+ if not name.startswith('*'):
+ self.memcfg[name] = Memory(name, int(origin, 16), int(length, 16), attrs[0] if attrs else '')
+
+ # Parse section information
+ toplevel_m = re.compile('^(\.[a-zA-Z0-9_.]+)\s+(0x[0-9a-fA-F]+)\s+(0x[0-9a-fA-F]+)')
+ secondlevel_m = re.compile('^ (\.[a-zA-Z0-9_.]+)\s+(0x[0-9a-fA-F]+)\s+(0x[0-9a-fA-F]+)\s+(.*)$')
+ secondlevel_linebreak_m = re.compile('^ (\.[a-zA-Z0-9_.]+)\n')
+ filelike = re.compile('^(/?[^()]*\.[a-zA-Z0-9-_]+)(\(.*\))?')
+ linebreak_section = None
+ for l in self:
+ # Toplevel section
+ match = toplevel_m.match(l)
+ if match:
+ name, offx, length = match.groups()
+ offx, length = int(offx, 16), int(length, 16)
+ self[offx].add_toplevel(name, offx, length)
+
+ match = secondlevel_linebreak_m.match(l)
+ if match:
+ linebreak_section, = match.groups()
+ continue
+
+ if linebreak_section:
+ l = ' {} {}'.format(linebreak_section, l)
+ linebreak_section = None
+
+ # Second-level section
+ match = secondlevel_m.match(l)
+ if match:
+ name, offx, length, misc = match.groups()
+ match = filelike.match(misc)
+ if match:
+ fn, obj = match.groups()
+ obj = obj.strip('()') if obj else None
+ offx, length = int(offx, 16), int(length, 16)
+ self[offx].add_obj(name, offx, length, fn, obj)
+
+
+if __name__ == '__main__':
+ import argparse
+ parser = argparse.ArgumentParser(description='Parser GCC map file')
+ parser.add_argument('mapfile', type=argparse.FileType('r'), help='The GCC .map file to parse')
+ parser.add_argument('-m', '--memory', type=str, help='The memory segments to print, comma-separated')
+ args = parser.parse_args()
+ mf = MapFile(args.mapfile.read())
+ args.mapfile.close()
+
+ mems = args.memory.split(',') if args.memory else mf.memcfg.keys()
+
+ for name in mems:
+ mem = mf.memcfg[name]
+ print('Symbols by file for memory', name)
+ for tot, fn in reversed(sorted( (tot, fn) for fn, tot in mem.totals.items() )):
+ print(' {:>8} {}'.format(tot, fn))
+ for length, offx, sec, obj in reversed(sorted(( (length, offx, sec, obj) for sec, obj, offx, length in
+ mem.files[fn] ), key=lambda e: e[0] )):
+ name = sec.name if sec else None
+ print(' {:>8} {:>#08x} {}'.format(length, offx, obj))
+ #print('{:>16} 0x{:016x} 0x{:016x} ({:>24}) {}'.format(name, origin, length, length, attrs))
+
diff --git a/fw/tools/mapvis.py b/fw/tools/mapvis.py
new file mode 100644
index 0000000..4a71222
--- /dev/null
+++ b/fw/tools/mapvis.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+
+from matplotlib import pyplot as plt
+
+f, ax = plt.subplots(1, figsize=(3, 8))
+bar_width = 1
+
+ax.bar([-bar_width/2], top, bottom=bottom, width=bar_width, label='foo')
+ax.set_xticks([0], [filename])
+ax.set_ylabel('Memory usage (B)')
+ax.set_xlabel('')
+
+ax.set_xlim([-bar_width/2, bar_width/2])
+ax.set_ylim([0, mem_max])
+
+if __name__ == '__main__':
+ import argparse
+ import mapparse
+ parser = argparse.ArgumentParser(description='Visualize program memory usage using GCC map file')
+ parser.add_argument('mapfile', type=argparse.FileType('r'), description='Input GCC .map file')
+ args = parser.parse_args()
+
+ mapping = mapparse.MapFile(args.mapfile.read())
+ mapping.
+
diff --git a/fw/tools/measure_transpose_performance.gdb b/fw/tools/measure_transpose_performance.gdb
new file mode 100644
index 0000000..966d69f
--- /dev/null
+++ b/fw/tools/measure_transpose_performance.gdb
@@ -0,0 +1,17 @@
+target remote localhost:3333
+file main.elf
+load
+set print pretty on
+set pagination off
+cont
+print cvr/50.0F
+cont
+print cvr/50.0F
+cont
+print cvr/50.0F
+cont
+print cvr/50.0F
+cont
+print cvr/50.0F
+cont
+quit
diff --git a/fw/tools/profile.gdb b/fw/tools/profile.gdb
new file mode 100644
index 0000000..fd26540
--- /dev/null
+++ b/fw/tools/profile.gdb
@@ -0,0 +1,5 @@
+set pagination off
+target remote localhost:3333
+while(1)
+ continue
+end
diff --git a/fw/tools/profile.sh b/fw/tools/profile.sh
new file mode 100644
index 0000000..b209a14
--- /dev/null
+++ b/fw/tools/profile.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+NLOOPS=1000
+SLEEP=0.1
+
+[ $# -lt 1 ] && echo "ERROR: Not enough arguments" && exit 2
+elffile="$1"
+
+trap "exit" SIGINT
+
+logfile=$(mktemp)
+
+arm-none-eabi-gdb -x profile.gdb "$elffile" > "$logfile" 2>/dev/null&
+gdbpid=$!
+trap "kill -TERM $gdbpid; rm $logfile" EXIT
+echo "Gathering..."
+for i in $(seq 1 $NLOOPS); do
+ echo "$i/$NLOOPS"
+ kill -INT $gdbpid
+ sleep $SLEEP
+done
+kill -TERM $gdbpid
+trap "rm '$logfile'" EXIT
+
+egrep -o '\w+ \(.*\) at .*' "$logfile" |cut -d' ' -f1|sort|uniq -c|sort -n
+echo 'Total:' $(egrep -c '\w+ \(.*\) at .*' "$logfile")
+
diff --git a/fw/tools/pymapfile b/fw/tools/pymapfile
new file mode 160000
+Subproject 7e0c9c46ecd0b9dc7a679ed2d445bca63d096be
diff --git a/fw/tools/scans/digit.png b/fw/tools/scans/digit.png
new file mode 100644
index 0000000..bfe9614
--- /dev/null
+++ b/fw/tools/scans/digit.png
Binary files differ
diff --git a/fw/tools/scans/megumin_segmentmap_600dpi.jpg b/fw/tools/scans/megumin_segmentmap_600dpi.jpg
new file mode 100644
index 0000000..8f8dbee
--- /dev/null
+++ b/fw/tools/scans/megumin_segmentmap_600dpi.jpg
Binary files differ
diff --git a/fw/tools/scans/pretty.jpg b/fw/tools/scans/pretty.jpg
new file mode 100644
index 0000000..806a6ed
--- /dev/null
+++ b/fw/tools/scans/pretty.jpg
Binary files differ
diff --git a/fw/tools/scans/pretty.png b/fw/tools/scans/pretty.png
new file mode 100644
index 0000000..47d6bfa
--- /dev/null
+++ b/fw/tools/scans/pretty.png
Binary files differ
diff --git a/fw/tools/scans/pretty2.png b/fw/tools/scans/pretty2.png
new file mode 100644
index 0000000..353dcb6
--- /dev/null
+++ b/fw/tools/scans/pretty2.png
Binary files differ
diff --git a/fw/tools/segmasks/seg1.png b/fw/tools/segmasks/seg1.png
new file mode 100644
index 0000000..96abf0c
--- /dev/null
+++ b/fw/tools/segmasks/seg1.png
Binary files differ
diff --git a/fw/tools/segmasks/seg2.png b/fw/tools/segmasks/seg2.png
new file mode 100644
index 0000000..be79d61
--- /dev/null
+++ b/fw/tools/segmasks/seg2.png
Binary files differ
diff --git a/fw/tools/segmasks/seg3.png b/fw/tools/segmasks/seg3.png
new file mode 100644
index 0000000..3d0715d
--- /dev/null
+++ b/fw/tools/segmasks/seg3.png
Binary files differ
diff --git a/fw/tools/segmasks/seg4.png b/fw/tools/segmasks/seg4.png
new file mode 100644
index 0000000..b783f3c
--- /dev/null
+++ b/fw/tools/segmasks/seg4.png
Binary files differ
diff --git a/fw/tools/segmasks/seg5.png b/fw/tools/segmasks/seg5.png
new file mode 100644
index 0000000..013fda8
--- /dev/null
+++ b/fw/tools/segmasks/seg5.png
Binary files differ
diff --git a/fw/tools/segmasks/seg6.png b/fw/tools/segmasks/seg6.png
new file mode 100644
index 0000000..08b45cc
--- /dev/null
+++ b/fw/tools/segmasks/seg6.png
Binary files differ
diff --git a/fw/tools/segmasks/seg7.png b/fw/tools/segmasks/seg7.png
new file mode 100644
index 0000000..dcd4d0f
--- /dev/null
+++ b/fw/tools/segmasks/seg7.png
Binary files differ
diff --git a/fw/tools/segmasks/seg8.png b/fw/tools/segmasks/seg8.png
new file mode 100644
index 0000000..1979630
--- /dev/null
+++ b/fw/tools/segmasks/seg8.png
Binary files differ
diff --git a/fw/tools/test.py b/fw/tools/test.py
new file mode 100755
index 0000000..63cb324
--- /dev/null
+++ b/fw/tools/test.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python3
+import serial
+import struct
+from itertools import takewhile
+
+def chunked(data, chunk_size):
+ for i in range(0, len(data), chunk_size):
+ yield data[i:i+chunk_size]
+
+def frame_packet(data):
+ if len(data) > 254:
+ raise ValueError('Input too long')
+ out = b''
+ for run in data.split(b'\0'):
+ out += bytes([len(run)+1])
+ out += run
+ out += b'\0'
+ return out
+
+def format_packet(data):
+ out = b''
+ for a, b, c, d, e, f, g, h in chunked(data, 8):
+ ah, bh, ch, dh = a>>8, b>>8, c>>8, d>>8
+ eh, fh, gh, hh = e>>8, f>>8, g>>8, h>>8
+ al, bl, cl, dl = a&0xff, b&0xff, c&0xff, d&0xff
+ el, fl, gl, hl = e&0xff, f&0xff, g&0xff, h&0xff
+ # FIXME check order of high bits
+ out += bytes([al, bl, cl, dl, el, fl, gl, hl,
+ (ah<<6 | bh<<4 | ch<<2 | dh<<0)&0xff,
+ (eh<<6 | fh<<4 | gh<<2 | hh<<0)&0xff])
+ out += bytes([1, 0, 0, 0]) # global intensity
+ return out
+
+def chariter(ser):
+ while True:
+ yield ser.read(1)
+
+def read_frame(ser):
+ return b''.join(takewhile(lambda c: c and c[0], chariter(ser)))
+
+def unstuff(data):
+ out = b''
+ while data:
+ stuff = data[0]
+ if out:
+ out += b'\0'
+ out += data[1:stuff]
+ data = data[stuff:]
+ return out
+
+def receive_frame(ser):
+ return unstuff(read_frame(ser))
+
+def mac_frame(mac):
+ return frame_packet(struct.pack('<I', mac))
+
+def send_framebuffer(ser, mac, frame):
+ formatted = format_packet(frame)
+ framed = mac_frame(mac) + frame_packet(formatted[:162]) + frame_packet(formatted[162:])
+ ser.write(framed)
+
+def discover_macs(ser, count=20):
+ found_macs = []
+ while True:
+ ser.flushInput()
+ ser.write(b'\0')
+ frame = receive_frame(ser)
+ if len(frame) == 4:
+ mac, = struct.unpack('<I', frame)
+ if mac not in found_macs:
+ print('Discovered new MAC: {:2} {:08x}'.format(len(found_macs), mac))
+ found_macs.append(mac)
+ if len(found_macs) == count:
+ return found_macs
+ elif len(frame) != 0:
+ print('Invalid frame of length {}:'.format(len(frame)), frame)
+ time.sleep(0.05)
+
+def parse_status_frame(frame):
+ print('frame len:', len(frame))
+ if not frame:
+ return None
+ ( firmware_version,
+ hardware_version,
+ digit_rows,
+ digit_cols,
+ uptime_s,
+ framerate_millifps,
+ uart_overruns,
+ frame_overruns,
+ invalid_frames,
+ vcc_mv,
+ temp_celsius,
+ nbits ) = struct.unpack('<4B5IhhB', frame)
+ del frame
+ return locals()
+
+def fetch_status(ser, mac):
+ ser.flushInput()
+ ser.write(mac_frame(mac))
+ ser.write(frame_packet(b'\x01'))
+ return parse_status_frame(receive_frame(ser))
+
+if __name__ == '__main__':
+ import argparse
+ import time
+ from binascii import hexlify
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('serial')
+ args = parser.parse_args()
+
+ ser = serial.Serial(args.serial, 2000000, timeout=0.05)
+
+ frame_len = 4*8*8
+ black, red = [0]*frame_len, [255]*frame_len
+ frames = \
+ [black]
+ #[[0]*i + [255]*(256-i) for i in range(257)]
+ #[[(i + d)%256 for d in range(frame_len)] for i in range(256)]
+ #[black]*10 +\
+ #[red]*10 +\
+ #[[i]*frame_len for i in range(256)] +\
+ #[[(i + (d//8)*8) % 256*8 for d in range(frame_len)] for i in range(256)]
+
+ #frames = [red, black]*5
+ #frames = [ x for l in [[([0]*i+[255]+[0]*(7-i))*32]*2 for i in range(8)] for x in l ]
+ found_macs = [0xdeadbeef] #discover_macs(ser, 1)
+ mac, = found_macs
+
+ import pprint
+ while True:
+ try:
+ pprint.pprint(fetch_status(ser, mac))
+ except e:
+ print(e)
+ for i, frame in enumerate(frames):
+ send_framebuffer(ser, mac, frame)
+ time.sleep(0.1)
+ # to produce framing errors: ser.write(b'\02a\0')
diff --git a/fw/tools/transpose_test.c b/fw/tools/transpose_test.c
new file mode 100644
index 0000000..793872d
--- /dev/null
+++ b/fw/tools/transpose_test.c
@@ -0,0 +1,77 @@
+
+#include <stdio.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <string.h>
+#include <assert.h>
+
+#include "transpose.h"
+
+void rxbuf_dump(struct data_format *rx) {
+ fprintf(stdout, "\033[38;5;244m high | low\n");
+ fprintf(stdout, " a b c d e f g . | a b c d e f g .\033[0m\n");
+
+ for (int i=0; i<32; i++) {
+ for (int j=0; j<8; j++)
+ fprintf(stdout, "%02x ", rx[i].high[j]);
+ fprintf(stdout, "\033[38;5;244m|\033[0m");
+ for (int j=0; j<8; j++)
+ fprintf(stdout, " %1x", (rx[i].low>>(2*j))&0x3);
+ fprintf(stdout, "\n");
+ }
+}
+
+void fb_dump(struct framebuf *fb) {
+ fprintf(stdout, "\033[38;5;244mbit | ");
+ for (int i=0; i<8; i++)
+ fprintf(stdout, " %d ", i);
+ fprintf(stdout, "\033[0m\n");
+
+ for (int i=0; i<10; i++) {
+ fprintf(stdout, " %2d |", i);
+ for (int j=0; j<8; j++)
+ fprintf(stdout, " %08x", fb->frame[i].data[j]);
+ fprintf(stdout, "\n");
+ }
+}
+
+int main(int argc, char **argv) {
+ FILE *randf = fopen("/dev/urandom", "r");
+ if (!randf)
+ return 2;
+
+ struct framebuf *fb = malloc(sizeof(struct framebuf));
+ if (!fb)
+ return 2;
+ assert(sizeof(*fb) == 32*sizeof(struct data_format)+4);
+
+ uint8_t *rxbuf1 = malloc(sizeof(*fb));
+ if (!rxbuf1)
+ return 2;
+ if (fread(rxbuf1, 1, sizeof(*fb), randf) != sizeof(*fb))
+ return 2;
+
+ uint8_t *rxbuf2 = malloc(sizeof(*fb));
+ if (!rxbuf2)
+ return 2;
+ memcpy(rxbuf2, rxbuf1, sizeof(*fb));
+
+ memset((void *)fb, 0, sizeof(*fb));
+
+ transpose_data(rxbuf1, fb);
+ untranspose_data(fb, rxbuf1);
+
+ assert(!memcmp(rxbuf1, rxbuf2, sizeof(fb)));
+
+ fprintf(stdout, "\n\033[93mDUMP of ORIGINAL\033[0m\n");
+ rxbuf_dump((struct data_format *)rxbuf2);
+
+ fprintf(stdout, "\n\033[93mDUMP of FRAME BUFFER\033[0m\n");
+ fb_dump(fb);
+
+ fprintf(stdout, "\n\033[93mDUMP of RESULT\033[0m\n");
+ rxbuf_dump((struct data_format *)rxbuf1);
+
+ fprintf(stderr, "PASS");
+ return 0;
+}