diff --git a/LICENSE.picoprobe-sump b/LICENSE.picoprobe-sump index 9232656..a8b463b 100644 --- a/LICENSE.picoprobe-sump +++ b/LICENSE.picoprobe-sump @@ -2,7 +2,10 @@ The below copyright and permission notice applies to portions of the following files, which have been modified from their original versions in (the "picoprobe-sump repository") - - TODO + - bsp/rp2040/m_sump/sump_hw.c + - bsp/rp2040/m_sump/sump_hw.h + - src/m_sump/cdc_sump.c + - src/m_sump/sump.h The below notice does not apply to any modifications made to the above files since the versions present in the picoprobe-usb repository, nor to any files diff --git a/README.md b/README.md index 2c3c31c..ec36923 100644 --- a/README.md +++ b/README.md @@ -136,30 +136,31 @@ Different serial speeds can be used, too. Serprog support is *techincally* untested, as in it does output the correct SPI commands as seen by my logic analyzer, but I don't have a SPI flash chip to test it on. -### I2C-Tiny-USB +### SPI, I2C and temperature sensor -The I2C-Tiny-USB functionality can be used as follows: first, load the -`i2c-dev` and `i2c-tiny-usb` modules (for now you need a patched version of the -latter, can be found in the `i2c-tiny-usb-misc/` folder in this repo). Then you -can use the I2C USB bridge as any other I2C device on your computer. For -example, the `i2cdetect`, `i2cget` and `i2cset` tools from `i2c-tools` should -all work. You can find which I2C device corresponds to the I2C-Tiny-USB, by -running `i2cdetect -l`: +This functionality depends on custom kernel modules being loaded: In the +`host/modules/` directory, one can find the sources and a Makefile. + +After loading the modules (and modprobing `i2c-dev` and `spidev`), devices for +these interfaces should appear in `/dev`. + +SPI and I2C can be controlled using the standard tools for these (eg. the +utilities from `i2c-tools` package), and the temperature sensor should show +up in `lm_sensors`. + +Using `i2cdetect -l`, you should be able to see which I2C device belongs to +the tool: ``` $ sudo i2cdetect -l [...] i2c-1 i2c i915 gmbus dpb I2C adapter i2c-8 i2c Radeon i2c bit bus 0x95 I2C adapter -i2c-15 i2c i2c-tiny-usb at bus 001 device 011 I2C adapter # <---- ! +i2c-15 i2c dmj-i2c-1-1:1.0 I2C adapter i2c-6 i2c Radeon i2c bit bus 0x93 I2C adapter i2c-13 i2c AUX C/DDI C/PHY C I2C adapter -[...] ``` -**NOTE**: I2C functionality sometimes breaks with certain USB hubs. If this is -the case, try unplugging and replugging the entire hub. - #### I2C temperature sensor emulation If the board/MCU has a builtin temperature sensor, a fake I2C device on the bus @@ -168,7 +169,7 @@ sensor (the exact sensor emulated is the Microchip MCP9808). To have it show up in `sensors`, do the following (with `BUSNUM` the number from the above `i2cdetect -l` output): ``` -$ ./dmctl.py /dev/ttyACM1 --i2ctemp 0x18 # need to give it an address first +$ ./dmctl.sh tempsensor --set 0x18 # need to give it an address first $ sudo modprobe jc42 $ # now tell the jc42 module that the device can be found at this address $ echo "jc42 0x18" | sudo tee /sys/bus/i2c/devices/i2c-BUSNUM/new_device @@ -185,38 +186,40 @@ Temperature readout may be a bit higher than the ambient temperature. ### Runtime configuration Several settings can be applied at runtime, using the `dmctl` Python script. -Settings are communicated over the Serprog USB serial port. - -The currently implemented options are: -- `support`: tells you which features this implementation/board supports -- `ctsrts`: Enable/disable CTS/RTS-based hardware flow control for the UART port -- `i2ctemp`: Get or set the I2C address of the fake I2C device of the temperature - sensor. Use 0 for getting the value, 0xff for disabling, and any - other for setting the address. The I2C device emulated is an MCP9808. - When setting a value, the old value is printed. +Settings are communicated over a vendor USB interface. ``` -usage: dmctl [-h] [-v] [--ctsrts [CTSRTS]] tty - -Runtime configuration control for DapperMime-JTAG - -positional arguments: - tty Path to DapperMime-JTAG Serprog UART device +$ ./dmctl.sh --help +usage: dmctl [-h] [--conn CONN] subcommand ... optional arguments: - -h, --help show this help message and exit - -v, --verbose Verbose logging (for this utility) - --ctsrts [CTSRTS] Enable or disable CTS/RTS flow control (--ctsrts [true|false]) - --i2ctemp [I2CTEMP] Control the builtin I2C temperature controller: get (0), - disable (-1/0xff) or set/enable (other) the current - status and I2C bus address - --support Get list of supported/implemented functionality + -h, --help show this help message and exit + --conn CONN Connection string. Either a dmj-char device in /dev, a USB + bus.device number, or a USB VID:PID pair. Defaults to trying + /dev/dmj-* (if there is only one), and cafe:1312 otherwise. + +subcommands: + For more info on each subcommand, run the program with 'subcommand --help' as + arguments. + + subcommand Command to send to the device + get-device-info + Shows device info + get-mode-info Shows mode info. A mode can optionally be specified, default + is the current mode. + set-mode Set the device mode + uart-cts-rts Get, enable/disable UART hardware flow control + tempsensor Get or set the IRC emulation enable/address of the + temperature sensor. + jtag-scan JTAG pinout scanner + sump-overclock + SUMP logic analyzer overclock settings ``` -example: +Example: ``` -$ ./dmctl.py /dev/ttyACM1 --ctsrts true +$ ./dmctl.py --conn cafe:1312 get-device-info ``` ## License @@ -227,18 +230,15 @@ ARM's CMSIS 5 code is licensed under the [Apache 2.0 license](https://opensource libco is licensed under the [ISC license](https://opensource.org/licenses/ISC) +Some code has been incorporated from the [DapperMime](https://github.com/majbthrd/DapperMime) +and [picoprobe-sump](https://github.com/perexg/picoprobe-sump) +projects. These respective licenses can be found in +[this](./LICENSE.dappermime) and [this](./LICENSE.picoprobe-sump) file. + ## TODO - [ ] A name - [ ] A (VID and) PID, and maybe better subclass & protocol IDs for the vnd cfg itf -- [x] More Pico SDK meta/buildinfo -- [x] CMSIS-DAP JTAG implementation -- [x] Flashrom/SPI support using Serprog - - Parallel ROM flashing support, too, by having the device switch into a - separate mode that temporarily disables all other IO protocols - - Not enough IO, rip. -- [x] UART with CTS/RTS flow control - - [x] Needs configurable stuff as well, as some UART interfaces won't use this. - [x] Debug interface to send printf stuff directly to USB, instead of having to use the UART interface as a loopback thing. - [ ] Second UART port for when stdio UART is disabled? @@ -248,35 +248,11 @@ libco is licensed under the [ISC license](https://opensource.org/licenses/ISC) parts do, but, laziness. - [x] 10-bit I2C address support (Needs poking at the Pico SDK, as it only supports 7-bit ones). -- [x] Better USB interface stuff, because I2C-Tiny-USB sucks and serprog can only - do flash chips instead of being a real spidev. General idea can probably be - taken from the DLN2 Linux drivers, except better (dynamic interface - signalled in the protocol (eg. does the device actually have I2C/SPI/..?), - dynamic I2C and SPI capabilities, add 1wire stuff, maybe yeet the GPIO bc - it'll be used for other stuff anyway, etc.). Means a custom Linux driver but - oh well, I2C-Tiny-USB needs patching either way. -- [ ] 1-wire using ↑ -- [x] A proper interface for sending commands etc. instead of shoehorning it - into Serprog. - - Can probably be included in the "Better USB interface stuff". +- [ ] 1-wire - [ ] make modes persistent? - [ ] JTAG pinout detector - https://github.com/cyphunk/JTAGenum - https://github.com/travisgoodspeed/goodfet/blob/master/firmware/apps/jscan/jscan.c -- [x] Host-side script that is an XVC (or hw_server) cable and communicates - with the device to perform the JTAG commands, because Vivado no likey - OpenOCD. - - CMSIS-DAP interface can be used directly, see CMSIS_5/CMSIS/DoxyGen/DAP/src/dap_USB_cmds.txt - - https://github.com/BerkeleyLab/XVC-FTDI-JTAG - - https://www.eevblog.com/forum/fpga/xilinx-jtag-and-tcf/ - - https://git.eclipse.org/c/tcf/org.eclipse.tcf.git/plain/docs/TCF%20Linux%20Agent%20Prototype.html - - http://www.eclipse.org/tcf/ - - https://debugmo.de/2012/02/xvcd-the-xilinx-virtual-cable-daemon/ - - https://github.com/Xilinx/XilinxVirtualCable/ - - https://github.com/derekmulcahy/xvcpi -- [x] SUMP logic analyzer mode? - - see also [this](https://github.com/perexg/picoprobe-sump) - - [ ] runtime config options for overclocking, logging - [ ] FT2232 emulation mode? - watch out, still need a vnd cfg interface! libftdi expects the following stuff: (TODO: acquire detailed protocol description) - interface 0 ("A"): index 1, epin 0x02, epout 0x81 diff --git a/dmctl.py b/dmctl.py deleted file mode 100755 index c29cadb..0000000 --- a/dmctl.py +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env python3 - -import argparse, serial, struct -from typing import * - -def auto_int(x): - return int(x, 0) - -class RTOpt(NamedTuple): - type: Callable[[Any], Any] - optid: int - desc: str - -supportmap = { - 1: "CMSIS-DAP", - 2: "UART", - 4: "I2C-Tiny-USB", - 8: "Temperature sensor", - - 0x80: "stdio USB-CDC debug interface" -} - -option_table = { - 'ctsrts': RTOpt(bool, 1, "Enable or disable CTS/RTS flow control (--ctsrts [true|false])"), - 'i2ctemp': RTOpt(auto_int, 2, "Control the builtin I2C temperature controller: get (0), disable (-1/0xff) or set/enable (other) the current status and I2C bus address"), - 'support': RTOpt(str, 0xff, "Get list of supported/implemented functionality"), -} - -S_ACK = b'\x06' -S_NAK = b'\x15' - -S_CMD_NOP = b'\x00' -S_CMD_Q_IFACE = b'\x01' -S_CMD_Q_CMDMAP = b'\x02' -S_CMD_Q_PGMNAME = b'\x03' -S_CMD_SYNCNOP = b'\x10' -S_CMD_MAGIC_SETTINGS = b'\x53' - -def val2byte(t, v) -> int: - if t == bool: - return 1 if v else 0 - if t == int or t == auto_int: - return 0xff if v < 0 else (v & 0xff) - if t == str: - return 0 - - assert False, "unimplemented type %s" % str(t) - -def do_xfer(args, cmd:int, arg:int, port: str, baudrate:int=115200) -> Optional[int]: - with serial.Serial(port, baudrate, timeout=1) as ser: - cmdmap = [0]*32 - syncok = False - for i in range(8): - ser.write(S_CMD_SYNCNOP) - a = ser.read() - b = ser.read() - if a == S_NAK and b == S_ACK: - syncok = True - break - - if not syncok: - print("sync failed") - return None - - ser.write(S_CMD_NOP) - if ser.read() != S_ACK: - print("nop failed") - return None - - ser.write(S_CMD_Q_IFACE) - if ser.read() != S_ACK: - print("q_iface failed") - return None - serprogver = struct.unpack('> 3] & (1<<(S_CMD_MAGIC_SETTINGS[0]&7))) == 0: - print("serprog programmer has no S_CMD_MAGIC_SETTINGS") - return None - - if (cmdmap[S_CMD_Q_PGMNAME[0] >> 3] & (1<<(S_CMD_Q_PGMNAME[0]&7))) != 0: - ser.write(S_CMD_Q_PGMNAME) - if ser.read() != S_ACK: - print("q_pgmname failed") - else: - name = ser.read(16).decode('utf-8') - if args.verbose: print("programmer is '%s'" % name) - - ser.write(S_CMD_MAGIC_SETTINGS) - ser.write(bytes([cmd,arg])) - acknak = ser.read() - - if acknak == S_ACK: - return ser.read()[0] - else: - print("settings command failed") - return None - -def main(): - parser = argparse.ArgumentParser(prog="dmctl", - description="Runtime configuration control for DapperMime-JTAG") - - parser.add_argument('tty', type=str, nargs=1, #'?', #default="/dev/ttyACM1", - help="Path to DapperMime-JTAG Serprog UART device"#+\ - #" [/dev/ttyACM1]" - ) - - parser.add_argument('-v', '--verbose', default=False, action='store_true', - help="Verbose logging (for this utility)") - - for k, v in option_table.items(): - if k == "support": - parser.add_argument('--%s'%k, default=None, action='store_true', - help=v.desc) - else: - parser.add_argument('--%s'%k, type=v.type, nargs='?', default=None, - help=v.desc) - - args = parser.parse_args() - - for k, v in option_table.items(): - if args.__dict__[k] is not None: - resp = do_xfer(args, v.optid, val2byte(v.type, args.__dict__[k]), args.tty[0]) - if resp is None: - return 1 - if k == "support": - print(", ".join(kvp[1] for kvp in supportmap.items() if (kvp[0] & resp) != 0)) - else: - #if args.verbose: - print("-> %d" % resp) - - return 0 - -#do_xfer(1, 1, "/dev/ttyACM1") -#do_xfer(1, 0, "/dev/ttyACM1") - -if __name__ == '__main__': - main() - diff --git a/dmctl2.py b/dmctl2.py deleted file mode 100755 index 08fb67f..0000000 --- a/dmctl2.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python3 - -# TODO: RIIR, probably - -import usb.core -from typing import * -import array - -print("find") -dev = usb.core.find(idVendor=0xcafe, idProduct=0x1312) - -#print("set config") -#dev.set_configuration() - -print("get config") -cfg = dev.get_active_configuration() -intf = cfg[(0,0)] - -print("get eps") -epout = usb.util.find_descriptor(intf, custom_match = lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT) -epin = usb.util.find_descriptor(intf, custom_match = lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN ) - -assert epout is not None -assert epin is not None - -def wrusb(ep, data: bytes): - return ep.write(data) -# TODO: buffering read? -def rdresp(ep) -> Tuple[int, bytes]: - acc = bytearray() - - # first stuff: header etc - arr = array.array('B') - arr.fromlist([0]*64) - nrd = ep.read(arr) - stat = arr[0] - plen = arr[1] - print("plen=0x%x"%plen) - if (plen & 0x80) != 0: - plen &= 0x7f - plen |= arr[2] << 7 - for x in arr.tobytes()[3:nrd]: - acc.append(x) - else: - for x in arr.tobytes()[2:nrd]: - acc.append(x) - - while len(acc) < plen: - for i in range(len(arr)): arr[i] = 0 - nrd = ep.read(arr) - for x in arr.tobytes()[:nrd]: - acc.append(x) - - return (stat, acc) - - -print("cmds!") -epout.write(b'\x00') # get version -print('[%s]'%(', '.join(hex(x) for x in epin.read(4)))) # result: status, payload len, version -# 0 2 0x10 0x00 -> ok - -epout.write(b'\x01') # get modes -print('[%s]'%(', '.join(hex(x) for x in epin.read(4)))) # result: status, payload len, modes -# 0 2 0x3 0 -> ok - -epout.write(b'\x02') # get cur mode -print('[%s]'%(', '.join(hex(x) for x in epin.read(3)))) # result: status, payload len, mode -# 0 1 1 -> ok - -epout.write(b'\x04') # get infostr -(stat, res) = rdresp(epin) -print("stat=%d"%stat) -print(res) - -epout.write(b'\x10') # get mode1 name -(stat, res) = rdresp(epin) -print("stat=%d"%stat) -print(res) - -epout.write(b'\x11') # get mode1 version -(stat, res) = rdresp(epin) -print("stat=%d"%stat) -print(res) - -epout.write(b'\x12') # get mode1 features -(stat, res) = rdresp(epin) -print("stat=%d"%stat) -print(res) - -epout.write(b'\x40') # get mode4 name -(stat, res) = rdresp(epin) -print("stat=%d"%stat) -print(res) - -epout.write(b'\x41') # get mode4 version -(stat, res) = rdresp(epin) -print("stat=%d"%stat) -print(res) - -epout.write(b'\x42') # get mode4 features -(stat, res) = rdresp(epin) -print("stat=%d"%stat) -print(res) - -### ATTEMPT A MODESET ### - -#epout.write(b'\x03\x04') # set cur mode -#print('[%s]'%(', '.join(hex(x) for x in epin.read(3)))) # result: status, payload len, mode - - diff --git a/dmctl3.py b/dmctl3.py deleted file mode 100755 index 5716737..0000000 --- a/dmctl3.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 - -import os, struct, sys - -f = os.open(sys.argv[1], os.O_RDWR | os.O_CLOEXEC) # TODO: windows: os.O_BINARY | -try: - os.write(f, b'\x00') # get version - resp = os.read(f, 4) # response: status, paylaod len (should be 2), payload - print("resp=%s"%repr(resp)) - print("stat=%d plen=%d ver=%04x" % (resp[0], resp[1], struct.unpack('