From embeddedTS Manuals
Product Page
Product Images
Mechanical Drawing
FTP Path
NXP i.MX286
454 MHz Arm®v5TE Arm926EJ-S™
i.MX286 Product Page
CPU Reference Manual


The TS-7600 was released April 2013. This is a small embedded board with an NXP i.MX286 454 Mhz ARM9 CPU, Lattice XP2 5k FPGA, and 128-256 MB DDR2 RAM.

Getting Started

A Linux PC is recommended for development, and will be assumed for this documentation. For users in Windows or OSX we recommend virtualizing a Linux PC. Most of our platforms run Debian and if there is no personal distribution preference this is what we recommend for ease of use.


Suggested Linux Distributions

It may be possible to develop using a Windows or OSX system, but this is not supported. Development will include accessing drives formatted for Linux and often Linux based tools.

Development Kit and Accessories

The KIT-7600 includes the items that are necessary for development with the TS-7600.

KIT-7600 Contents
Item Description
MSD-4GB-USB-7600 A Sandisk MicroSD card with a Vivitar SD reader. We recommend Sandisk SD cards as that is what we use for testing. Whenever we receive batches of SD cards from our suppliers, we will pull a few cards for testing to ensure they behave within our expectations. The Vivitar reader is also recommended because it was tested to work with the most SD cards, and it does not have a potentially damaging voltage drop that many consumer SD readers have.
TS-752 The TS-752 demonstrates the flexibility of a Technologic Systems SoM by connecting FPGA driven IO pins to relays, buffered digital inputs, buffered digital outputs, and RS-485 drivers. It also provides an RS-232 driver for the serial console. A TS-ENC750 with a TS-7600 can provide a complete solution for many embedded applications.
PS-5VDC-REG-8PG The PS-5VDC-REG-8PG is a 5V 2.5A power supply by Condor. Optionally type I or C adapters are available and will ship with the product if ordered to a country where this specific adapter is required. If you require one of these adapters it is recommended to put this in the comments for your order.
CB7-05 The CB7-05 is a 5 foot null modem cable. This is commonly used to connect to your workstation.
CB-USB-AMBM This is a USB A male to USB B male which is commonly used to connect the board to your PC as a USB device. This is also used for connecting the TS-9449 to your workstation for a USB to serial console.

The other options available in addition to the kit include:

Item Description
CN-PC104-40PIN-F The CN-PC104-40PIN-F is the mating connector for the 44 pin male header on the SBC. The 4 omitted pins are the JTAG pins which are only used for factory programming. The pins on this header are a very common 0.100" pitch.
TS-ENC750 The TS-ENC750 provides both the TS-752 and a metal enclosure.
TS-ENC750-DIN The TS-ENC750-DIN is the TS-ENC750 with a DIN rail mount.
TS-752 The TS-752 demonstrates the flexibility of a Technologic Systems SoM by connecting FPGA driven IO pins to relays, buffered digital inputs, buffered digital outputs, and RS-485 drivers. It also provides an RS-232 driver for the serial console. A TS-ENC750 with a TS-7600 can provide a complete solution for many embedded applications.

Booting up the board

WARNING: Be sure to take appropriate Electrostatic Discharge (ESD) precautions. Disconnect the power source before moving, cabling, or performing any set up procedures. Inappropriate handling may cause damage to the board.

The TS-7600 has two ways that it can be powered. The TS-7600 has a 5 V line on the 44 Pin Header which can be used to supply power. The TS-752 baseboard can power the 5 V rail through the terminal blocks with the PS-5VDC-REG-8PG. The second option is to use the USB Device port, most USB hosts can provide 500 mA at 5 V. The TS-7600 will require approximately 250 mA at idle. An ideal power supply for the TS-7600 will allow up to 1 A.

WARNING: Do not use multiple power connections simultaneously, you may damage the board and/or the power supplies

Once you have applied power you should look for console output. The first output is from the bootrom:

>> TS-BOOTROM - built Sep  4 2013 14:49:24                                               
>> Copyright (c) 2013, Technologic Systems                                               
LLCLLLLLLLFLCLLJUncompressing Linux... done, booting the kernel.                         
Booted in 3.89s                                                                          
Initramfs Web Interface: http://ts7600-4f3029.local                                        

The i.MX28 internal bootrom prints out the strings of letters to indicate various stages of its internal process. The TS-BOOTROM build date reflects when the imx-bootlets were built. When building a custom kernel from source this date will be changed and may not always reflect the kernel build date.

When power is first applied to the unit, the logic values on the MODE1 and MODE2 pins are latched. These signals direct the platform to load the kernel and initramfs from either the microSD, or onboard SPI (see table below). Note that in either case, the Debian distribution is loaded from the microSD card by default and this is required for booting the platform to any point including the initramfs. Meaning an imaged microSD card is still required when loading the kernel and initramfs from onboard SPI. This is due to start up scripts that are a part of the initramfs needing resources from the Debian filesystem.

If you are using the TS-752 the MODE1 and MODE2 pins are controlled by JP1 and JP2. Connecting the jumper will pull these to 0. The MODE1 and MODE2 signals both have 4.7k pull-ups. For a logic 0 on a custom baseboard these should be pulled to ground with a 680ohm resistor.

Boot Modes
Booot Device MODE1 MODE2
Onboard Flash 1 x[1]
SD Card 0 1
  1. "Don't Care."

Get a Console

Option 1: Telnet

If your system is configured with zeroconf support (Avahi, Bonjour, etc) you can simply connect with:

telnet ts7600-<last 6 characters of the MAC address>.local
# You will need to use your TS-7600 MAC address, but 
# for example if you mac is 00:d0:69:01:02:03
telnet ts7600-010203.local

When the board first powers up it has two network interfaces. The first interface eth0 is configured to use IPv4LL, and eth0:0 is configured to use DHCP. The board broadcasts using multicast DNS advertising the _telnet._tcp service. You can use this to query all of the available TS-7600s on the network.

From Linux you can use the avahi commands to query for all telnet devices with:

avahi-browse _telnet._tcp

Which would return:

+   eth0 IPv4 TS-7600 console [4f47a5]                      Telnet Remote Terminal local
+   eth0 IPv4 TS-7600 console [4f471a]                      Telnet Remote Terminal local

This will show you the mac address you can use to resolve the board. In this case you can connect to either ts7600-4f47a5.local or ts7600-4f471a.local

From Windows you can use Bonjour Print Services to get the dns-sd command. OSX also comes preinstalled with the same command. Once this is installed you can run:

dns-sd -B _telnet._tcp

Which will return:

Browsing for _telnet._tcp
Timestamp     A/R Flags if Domain                    Service Type              Instance Name
10:27:57.078  Add     3  2 local.                    _telnet._tcp.             TS-7600 console [4f47a5]
10:27:57.423  Add     3  2 local.                    _telnet._tcp.             TS-7600 console [4f471a]

This will show you the mac address you can use to resolve the board. In this case you can connect to either ts7600-4f47a5.local or ts7600-4f471a.local

Option 2: Serial Console

With the development kit you have the, TS-752 which brings out the debug console ttyS0 from the ARM processor as RS232. Custom baseboards should emulate the TS-752 for bringing out console. See the schematics available on the TS-752 page. The console from the UART will use 115200 baud, 8n1 (8 data bits 1 stop bit), and no flow control.

Console from Linux

There are many serial terminal applications for Linux, three common used applications are picocom, screen, and minicom. These examples demonstrate all three applications and assume that the serial device is "/dev/ttyUSB0" which is common for USB adapters. Be sure to replace the serial device string with that of the device on your workstation.

picocom is a very small and simple client.

sudo picocom -b 115200 /dev/ttyUSB0

screen is a terminal multiplexer which happens to have serial support.

sudo screen /dev/ttyUSB0 115200

Or a very commonly used client is minicom which is quite powerful but requires some setup:

sudo minicom -s
  • Navigate to 'serial port setup'
  • Type "a" and change location of serial device to "/dev/ttyUSB0" then hit "enter"
  • If needed, modify the settings to match this and hit "esc" when done:
     E - Bps/Par/Bits          : 115200 8N1
     F - Hardware Flow Control : No
     G - Software Flow Control : No
  • Navigate to 'Save setup as dfl', hit "enter", and then "esc"

Console from Windows

Putty is a small simple client available for download here. Open up Device Manager to determine your console port. See the putty configuration image for more details.

Device Manager Putty Configuration


When the unit first boots and is connected to a serial console, the output will look similar to the following:

>> TS-BOOTROM - built Sep  4 2013 14:49:24                                               
>> Copyright (c) 2013, Technologic Systems                                               
LLCLLLLLLLFLCLLJUncompressing Linux... done, booting the kernel.                         
Booted in 3.89s                                                                          
Initramfs Web Interface: http://ts7600-4f3029.local                                        
Note: Version dates may be different depending on shiped date and the image used.

This is a minimalistic initial RAM filesystem that includes our specific utilities for the unit, and is then used to bootstrap the Linux root. The initramfs is built into the kernel image so it cannot be modified without rebuilding the kernel, but it does include 8 bits for common configuration option we call soft jumpers. These soft jumpers are set in non-volatile memory inside of the FPGA and provide persistent bits regardless of the boot media.

Soft Jumpers
Jumper Function
1 Boot automatically to Debian [1]
2 Reserved
3 Reserved
4 Enable AUFS unification on root filesystem[2]
5 Reserved
6 Reserved
7 Boot as fast as possible [3]
8 Reserved
  1. initramfs boot is default
  2. REQUIRES using the 3.14 kernel and the secondary branch mentioned in the compile instructions.
  3. This will skip all setup of networking, baseboard detection and configuration, and is not compatible with DoubleStore boot devices

There are 2 ways to manipulate soft jumpers on the board. The web interface at "http://<model>-<last 6 chars of the MAC>.local" has a list of checkboxes that will immediately change the values. The built-in 'tshwctl' command can also be used for this.

# Boot automatically to Debian:
tshwctl --setjp=1

# Or revert to the initramfs:
tshwctl --removejp=1

If a serial console is not being used, we recommend ensuring Debian's network settings are configured first before booting directly to Debian by setting JP1. Once JP1 is enabled, the initramfs does not run any network configuration utilities and Debian must set the network. See Debian's Configuring the Network section for more information.

For most development, we recommend booting to Debian. This can be accomplished by typing "exit" through the serial or telnet console from the initramfs. As discussed, setting soft jumper JP1 will cause the unit to boot automatically to Debian on future boots. Additionally, many applications are capable of running from the initramfs. The initramfs itself cannot be easily modified, and it is not recommended to do so. The initramfs however has several hooks for applications to use. Debian is mounted at "/mnt/root/" as a read only filesystem which includes the "ts/" directory which offers several hooks and config files. Note that once booted to Debian, these files are available at "/ts/" rather than "/mnt/root/ts/".


For headless applications you can create a bash script with any initialization you require in /ts/init. This does not use the same $PATH as Debian, so you should enter the full path to any applications you intend to run from this environment. The init file does not exist by default and must be created:


/path/to/your/application &

USB update mechanism, tsinit

For implementing a custom production process or applying updates in the field, this SBC is capable of detecting a USB device and running a script. The behavior of this process can be tuned, see the config information section below. There are a few requirements of this script: the USB device itself must be the first or only USB storage device connected, the script must be on the first partition of said USB device and be named "tsinit", and the script must be world executable. PATH is passed to the tsinit script, it is what the initramfs environment PATH is, if additional PATHs are required that can be added in the tsinit script. Standard in/out are the standard console port, this means either the serial debug port, or telnet. Please note that if using telnet, output may be missed as the scripts will not wait for a telnet connection to establish, it is recommended to use a real serial port because of this.

During the USB update process, the first partition of the USB drive is mounted to "/mnt/usbdev/" of the initramfs. Then the update mechanism attempts to execute "/mnt/usbdev/tsinit" and thus launches the script. As stated above, PATH is passed to the script as well as standard in and out (linked to the console port) allowing for printed information in the script to appear on the terminal.


Graphical applications should use /ts/initramfs-xinit. The xinit file is used to start up a window manager and any applications. The default initramfs-xinit starts a webbrowser viewing localhost:

# Causes .Xauthority and other temp files to be written to /root/ rather than default /
export HOME=/root/
# Disables icewm toolbars
export ICEWM_PRIVCFG=/mnt/root/root/.icewm/

# minimalistic window manager
icewm-lite &

# this loop verifies the window manager has successfully started
while ! xprop -root | grep -q _NET_SUPPORTING_WM_CHECK
    sleep 0.1

# This launches the fullscreen browser.    If the xinit script ever closes, x11 will close.  This is why the last
# command is the target application which is started with "exec" so it will replace the xinit process id.
exec /usr/bin/fullscreen-webkit http://localhost


This config file can be used to alter many details of the initramfs boot procedure.

## This file is included by the early init system to set various bootup settings.
## if $jp7 is enabled none of these settings will be used.

## Used to control whether the FPGA is reloaded through software.
## 1 to enable reloading (default)
## 0 to disable reloading

## By default dns-sd is started which advertises the ts<model>-<last 6 of mac> 
## telnet and http services using zeroconf.
## 1 to enable dns-sd (default)
## 0 to disable dns-sd

## This is used to discover hosts and advertise this host over multicast DNS.
## 1 to enable mdns (default)
## 0 to disable mdns

## ifplugd is started in the initramfs to start udhcpc, and receive an ipv4ll
## address.
## 1 to enable ifplugd (default)
## 0 to disable ifplugd

## By default telnet is started on port 2323.
## 1 to enable telnet (default)
## 0 to disable telnet

## The busybox webserver is used to display a diagnostic web interface that can
## be used for development tasks such as rewriting the SD or uploading new
## software
## 1 to enable (default)
## 0 to disable

## This eanbles a reset switch on DIO 29 (TS-7700), or DIO 9 on all of the 
## boards.  Pull low to reset the board immediately.
## 1 to enable the reset sw (default)
## 0 to disable

## The console is forwarded through xuartctl which makes the cpu console available
## over telnet or serial console.
## 1 to enable network console (default)
## 0 to disable network console

## By default Alsa will put the SGTL5000 chip into standby after 5 seconds of 
## inactivity.  This is desirable in that it results in lower power consumption,
## but it can result in an audible popping noise.  This setting prevents 
## standby so the pop is never heard.  
## 1 to disable standby
## 0 to enable standby (default)

## xuartctl is used to access the FPGA uarts.  By default it is configured to
## be IRQ driven which is optimized for best latency, but at the cost of 
## additional CPU time.  You can reduce this by specifying a polling rate.
## The xuartctl process also binds to all network interfaces which can provide a 
## simple network API to access serial ports remotely.  You can restrict this to
## the local network with the bind option.
## Configure XUART polling 100hz
## Default is IRQ driven
## Configure xuartctl to bind on localhost
## Default binds on all interfaces
#CFG_XUARGS="--bind --irq=100hz"
## For a full list of arguments, see the xuartctl documentation here:
## http://docs.embeddedts.com/wiki/Xuartctl#Usage

## By default the system will probe for up to 10s on USB for a mass storage device
## and mount the first partition.  If there is an executable /tsinit script in the
## root this will be executed.  This is intended for production or updates.
## 2 to enable USB init always (adds 10s or $CFG_USBTIME to startup)
## 1 to enable USB init when jp1=0 (default)
## 0 to disable USB init always

## The USB init script by default blocks for 10s to detect a thumb drive that 
## contains the tsinit script.  Most flash media based drives can be detected 
## in 3s or less.  Some spinning media drives can take 10s, or potentially longer.
## This options is the number of seconds to wait before giving up on the 
## mass storage device.

### TS-8700
## Using the TS-8700 baseboard the board will by default initialze all of the 
## ethernet ports as individual vlan ports, eg eth0.1, eth0.2, eth0,3, and eth0.4
## The alterantive option sets Port A to eth0.1, and Ports B-D to eth0.2, or
## you can configure all ethernet ports as a single eth0 port.
## See http://docs.embeddedts.com/wiki/TS-8700 for more information
## 2 disables any vlan and passes through all interfaces to eth0
## 1 enables "WLAN" mode setting "A" as eth0.1, and all others as eth0.2
## 0 enables "VLAN" mode for 4 individual ports (default)

### TS-4712 / TS-4720
## These boards include an onboard switch with 2 external ports.  By default
## the switch will detect if it is on a known baseboard that supports the second
## ethernet switch port, and set up VLAN rules to define eth0.1 and eth0.2.  The
## other option is to configure the switch to pass through the packets to eth0
## regarless of port.
## 2 Disable VLAN and pass through to eth0
## 1 Enable VLAN on all baseboards
## 0 Enable VLAN on supported baseboards (Default)

First Linux Boot

Once the initramfs is exited, the unit will continue to boot to Debian. After Debian is booted, it will ask the user to log in with a username and password. This uses "root" as the username with no password by default. This can be changed after logging in with the command 'passwd' to set an account password. Note that this login will only work over the serial console. Logging in via SSH requires a password to be set.

Debian Configuration

For development, it is recommended to work directly in Debian on the SD card. Debian provides many more packages and a much more familiar environment for users already versed in Debian. Through Debian it is possible to configure the network, use the 'apt-get' suite to manage packages, and perform other configuration tasks. Out of the box the Debian distribution does not have any default username/password set. The account "root" is set up with no password configured. It is possible to log in via the serial console without a password but many services such as ssh will require a password set or will not allow root login at all. It is advised to set a root password and create a user account when the unit is first booted.

Note: Setting up a password for root is only feasible on the uSD image.

It is also possible to cross compile applications. Using a Debian host system will allow for installing a cross compiler to build applications. The advantage of using a Debian host system comes from compiling against libraries. Debian cross platform support allows one to install the necessary development libraries on the host, building the application on the host, and simply installing the runtime libraries on the target device. The library versions will be the same and completely compatible with each other. See the respective Debian cross compiling section for more information.

Configuring the Network

From almost any Linux system you can use "ip" or the ifconfig/route commands to initially set up the network. To configure the network interface manually you can use the same set of commands in the initramfs or Debian.

# Bring up the CPU network interface
ifconfig eth0 up

# Or if you're on a baseboard with a second ethernet port, you can use that as:
ifconfig eth1 up

# Set an ip address (assumes subnet mask)
ifconfig eth0

# Set a specific subnet
ifconfig eth0 netmask

# Configure your route.  This is the server that provides your internet connection.
route add default gw

# Edit /etc/resolv.conf for your DNS server
echo "nameserver" > /etc/resolv.conf

Most commonly networks will offer DHCP which can be set up with one command:

Configure DHCP in Debian:

# To setup the default CPU ethernet port
dhclient eth0
# Or if you're on a baseboard with a second ethernet port, you can use that as:
dhclient eth1
# You can configure all ethernet ports for a dhcp response with

Configure DHCP in the initrd:

udhcpc -i eth0
# Or if you're on a baseboard with a second ethernet port, you can use that as:
udhcpc -i eth1

To make your network settings take effect on startup in Debian, edit /etc/network/interfaces:

 # Used by ifup(8) and ifdown(8). See the interfaces(5) manpage or 
 # /usr/share/doc/ifupdown/examples for more information.          
 # We always want the loopback interface.                          
 auto lo                                                           
 iface lo inet loopback                                            
 auto eth0                                                         
 iface eth0 inet static                                            
 auto eth1                                                         
 iface eth1 inet dhcp
Note: During Debian's startup it will assign the interfaces eth0 and eth1 to the detected mac addresses in /etc/udev/rules.d/70-persistent-net.rules. If the system is imaged while this file exists it will assign the new interfaces as eth1 and eth2. This file is generated automatically on startup, and should be removed before your first software image is created. The initrd network configuration does not use this file.

In this example eth0 is a static configuration and eth1 receives its configuration from the DHCP server. For more information on network configuration in Debian see their documentation here.

WIFI Client

This board optionally supports 802.11 through the WIFI-N-USB-2 module using the ath9k_htc driver.

Scan for a network

ifconfig wlan0 up

# Scan for available networks
iwlist wlan0 scan

In this case I'm connecting to "default" which is an open network:

          Cell 03 - Address: c0:ff:ee:c0:ff:ee
                    Encryption key:off
                    Bit Rates:9 Mb/s

To connect to this open network:

iwconfig wlan0 essid "default"

You can use the iwconfig command to determine if you have authenticated to an access point. Before connecting it will show something similar to this:

# iwconfig wlan0
wlan0     IEEE 802.11bgn  ESSID:"default"  
          Mode:Managed  Frequency:2.417 GHz  Access Point: c0:ff:ee:c0:ff:ee   
          Bit Rate=1 Mb/s   Tx-Power=20 dBm   
          Retry  long limit:7   RTS thr:off   Fragment thr:off
          Encryption key:off
          Power Management:off
          Link Quality=70/70  Signal level=-34 dBm  
          Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
          Tx excessive retries:0  Invalid misc:0   Missed beacon:0

If you are connecting using WEP, you will need to define a network key:

iwconfig wlan0 essid "default" key "yourpassword"

If you are connecting to WPA you will need to use wpa_passphrase and wpa_supplicant:

wpa_passphrase the_essid the_password > /etc/wpa_supplicant.conf

Now that you have the configuration file, you will need to start the wpa_supplicant daemon:

wpa_supplicant -Dwext -iwlan0 -c/etc/wpa_supplicant.conf -B

Now you are connected to the network, but this would be close to the equivalent of connecting a network cable. To connect to the internet or talk to your internal network you will need to configure the interface. See the #Configuring the Network for more information, but commonly you can just run:

dhclient wlan0
Note: Some older images did not include the "crda" and "iw" packages required to make a wireless connection. If you cannot get an ip address you may want to connect over ethernet and install these packages with "apt-get install crda iw -y".

Host a WIFI Access Point

The software image includes a build of compat-drivers from 3.8 so a large amount of wireless devices are supported. Some devices support AP/Master mode which can be used to host an access point. The WIFI-N-USB-2 module we provide also supports this mode.

First install hostapd to manage the access point:

apt-get update && apt-get install hostapd -y

Edit /etc/hostapd/hostapd.conf to include:

Note: Refer to the kernel's hostapd documentation for more wireless configuration options.

To start the access point launch hostapd:

hostapd /etc/hostapd/hostapd.conf &

This will create a valid wireless access point, however many devices will not be able to connect without either a static connection, or a DHCP server. Refer to Debian's documentation for more details on DHCP configuration.

Installing New Software

Debian provides the apt-get system which manages pre-built applications. Before packages can be installed, the list of package versions and locations needs to be updated. This assumes the device has a valid network connection to the internet.

Debian Wheezy has been moved to archive status, this requires an update of /etc/apt/sources.list to contain only the following lines:

 deb http://archive.debian.org/debian wheezy main non-free
 deb-src http://archive.debian.org/debian wheezy main non-free
apt-get update
apt-get install --allow-unauthenticated debian-archive-keyring
apt-get update

For example, lets say you wanted to install openjdk for Java support. You can use the apt-cache command to search the local cache of Debian's packages.

 <user>@<hostname>:~# apt-cache search openjdk                                                                                  
 icedtea-6-jre-cacao - Alternative JVM for OpenJDK, using Cacao                                                           
 icedtea6-plugin - web browser plugin based on OpenJDK and IcedTea to execute Java applets                                 
 openjdk-6-dbg - Java runtime based on OpenJDK (debugging symbols)                                                        
 openjdk-6-demo - Java runtime based on OpenJDK (demos and examples)                                                      
 openjdk-6-doc - OpenJDK Development Kit (JDK) documentation                                                              
 openjdk-6-jdk - OpenJDK Development Kit (JDK)                                                                            
 openjdk-6-jre-headless - OpenJDK Java runtime, using Hotspot Zero (headless)                                             
 openjdk-6-jre-lib - OpenJDK Java runtime (architecture independent libraries)                                            
 openjdk-6-jre-zero - Alternative JVM for OpenJDK, using Zero/Shark                                                       
 openjdk-6-jre - OpenJDK Java runtime, using Hotspot Zero                                                                 
 openjdk-6-source - OpenJDK Development Kit (JDK) source files                                                            
 openoffice.org - office productivity suite                                                                               
 freemind - Java Program for creating and viewing Mindmaps                                                                
 default-jdk-doc - Standard Java or Java compatible Development Kit (documentation)                                       
 default-jdk - Standard Java or Java compatible Development Kit                                                           
 default-jre-headless - Standard Java or Java compatible Runtime (headless)                                               
 default-jre - Standard Java or Java compatible Runtime                                                                   

In this case you will likely want openjdk-6-jre to provide a runtime environment, and possibly openjdk-6-jdk to provide a development environment. You can often find the names of packages from Debian's wiki or from just searching on google as well.

Once you have the package name you can use apt-get to install the package and any dependencies. This assumes you have a network connection to the internet.

apt-get install openjdk-6-jre
# You can also chain packages to be installed
apt-get install openjdk-6-jre nano vim mplayer

For more information on using apt-get refer to Debian's documentation here.

Setting up SSH

On our boards we include the Debian package for openssh-server, but we remove the automatically generated keys for security reasons. To regenerate these keys:

dpkg-reconfigure openssh-server

Make sure your board is configured properly on the network, and set a password for your remote user. SSH will not allow remote connections without a password or a shared key.

Note: Setting up a password for root is only feasible on the uSD image.
passwd root

You should now be able to connect from a remote Linux or OSX system using "ssh" or from Windows using a client such as putty.

Note: If your intended application does not have a DNS source on the target network, it can save login time to add "UseDNS no" in /etc/ssh/sshd_config.

Starting Automatically

From Debian the most straightforward way to add your application to startup is to create a startup script. This is an example simple startup script that will toggle the red led on during startup, and off during shutdown. In this case I'll name the file customstartup, but you can replace this with your application name as well.

Edit the file /etc/init.d/customstartup to contain this:

 #! /bin/sh
 # /etc/init.d/customstartup
 case "$1" in
     ## If you are launching a daemon or other long running processes
     ## this should be started with
     # nohup /usr/local/bin/yourdaemon &
     # if you have anything that needs to run on shutdown
     echo "Usage: customstartup start|stop" >&2
     exit 3
 exit 0
Note: The $PATH variable is not set up by default in init scripts so this will either need to be done manually or the full path to your application must be included.

To make this run during startup and shutdown:

update-rc.d customstartup defaults

To manually start and stop the script:

/etc/init.d/customstartup start
/etc/init.d/customstartup stop

While this is useful for headless applications, if you are using X11 you should modify "/usr/bin/default-x-session":


export HOME=/root/
export ICEWM_PRIVCFG=/mnt/root/root/.icewm/

icewm-lite &

while ! xprop -root | grep -q _NET_SUPPORTING_WM_CHECK
    sleep 0.1

exec /usr/bin/fullscreen-webkit

Replace fullscreen-webkit with your own graphical application.

Creating a Custom Startup Splash

The default image includes a splash image that displays the TS logo. You can replace this with your own logo by replacing the files in /ts/splash/, or disable the splash screen by removing these files. The fbsplash utility that displays the splash logo in a ppm format. You can use graphics applications such as Gimp which can export to ppm, or you can use imagemagick in Linux to convert another file to ppm:

convert splash.png splash.ppm

The image resolution usually should match the screen, but otherwise it will be aligned to the upper left corner. If the system is configured to automatically boot to Debian it will display the splash screen until X11 is started.

Backup / Restore

If you are using a Windows workstation there is no support for writing directly to block devices. However, as long as one of your booting methods still can boot a kernel and the initrd you can rewrite everything by using a usb drive. This is also a good way to blast many stock boards when moving your product into production. You can find more information about this method with an example script here.

Note: Note that the MBR installed by default on this board contains a 446 byte bootloader program that loads the initial power-on kernel and initrd from the first and second partitions. Replacing it with an MBR found on a PC would not work as a PC MBR contains an x86 code bootup program.

MicroSD Card

MicroSD.png Click to download the latest 4GB SD card image.

Using another Linux workstation

If you do not have an SD card that can boot to the initramfs, you can download the latest SD card image and rewrite this from a Linux workstation. A USB MicroSD adapter can be used to access the card. First, you must find out which /dev/ device corresponds with your USB reader/writer.

Step 1 Option 1 (lsblk)

Newer distributions include a utility called "lsblk" which allows simple identification of the intended card:

 sdY      8:0    0   400G  0 disk 
 ├─sdY1   8:1    0   398G  0 part /
 ├─sdY2   8:2    0     1K  0 part 
 └─sdY5   8:5    0     2G  0 part [SWAP]
 sr0     11:0    1  1024M  0 rom  
 sdX      8:32   1   3.9G  0 disk 
 ├─sdX1   8:33   1   7.9M  0 part 
 ├─sdX2   8:34   1     2M  0 part 
 ├─sdX3   8:35   1     2M  0 part 
 └─sdX4   8:36   1   3.8G  0 part  

In this case the SD card is 4GB, so sdX is the target device. Note that on your system, sdX will not be a real device, it could be sda, sdb, mmcblk0, etc. Technologic Systems is not responsible for any damages cause by using the improper device node for imaging an SD card.

Step 1 Option 2 (dmesg)

After plugging in the device, you can use dmesg to list

 scsi 54:0:0:0: Direct-Access     Generic  Storage Device   0.00 PQ: 0 ANSI: 2
 sd 54:0:0:0: Attached scsi generic sg2 type 0
 sd 54:0:0:0: [sdX] 3862528 512-byte logical blocks: (3.97 GB/3.84 GiB)

In this case, sdXc is shown as a 3.97GB card. Note that on your system, sdX will not be a real device, it could be sda, sdb, mmcblk0, etc. Technologic Systems is not responsible for any damages cause by using the improper device node for imaging an SD card.

Step 2

Once you have the target /dev/ device you can use "dd" to backup/restore the card. To restore the board to stock, or rewrite to the latest SD image:

wget https://files.embeddedTS.com/ts-arm-sbc/ts-7600-linux/binaries/ts-images/ts4600_7600-latest-4GB.dd.bz2

# Specify your block device instead of /dev/sdX
# Note that this is a whole disk image, so use /dev/sdX instead of
# using /dev/sdX1
bzcat ts4600_7600-latest-4GB.dd.bz2 | dd conv=fsync bs=4M of=/dev/sdX

To take a backup of your entire SD card, you can switch the input file and the output file:

# Specify your block device instead of /dev/sdX
dd if=/dev/sdX conv=fsync bs=4M | bzip2 > backup.dd.bz2

SPI Flash

The SPI flash on-board can be used as a boot device by storing a bootable copy of the i.MX28 bootstream (bootrom, kernel, and initramfs combined). The SPI flash can only be programmed from a booted SBC.

The nov052013 Image (and later) includes a command that can be run from the initramfs to flash the i.MX28 bootstream to the SPI flash using a bootstream image on the SD card located at /mnt/root/lib/modules/imx28_ivt_linux.spi This file is generated and installed during kernel compilation. When booted to the initramfs, enter the following command:


The SPI flash can be manually programmed as well, see the following commands for a pre-nov052013 image, or to flash a SPI bootstream that is not located at /mnt/root/lib/modules/imx28_ivt_linux.spi

Download the latest SPI bootstream image on a booted SBC. Then, run the following commands:

wget https://files.embeddedTS.com/ts-arm-sbc/ts-7600-linux/binaries/ts-images/ts4600_7600-latest.spi
spiflashctl --lun 0 --erase --verify
spiflashctl --lun 0 --verify -W16384 -z512 -i ts4600_7600-latest.spi

Note that this SPI flash interface is implemented though DIO bitbanging, which can result in slower speeds. While the CPU has an SPI peripheral that this SPI flash is connected to, the peripheral is electrically connected to the second SD card and the driver for this takes control of the peripheral. Speeds in writing to the SPI flash can vary, however it averages around 100kb/s

Software Development

Most of our examples are going to be in C, but Debian will include support for many more programming languages. Including (but not limited to) C++, PERL, PHP, SH, Java, BASIC, TCL, and Python. Most of the functionality from our software examples can be done from using system calls to run our userspace utilities. For higher performance, you will need to either use C/C++ or find functionally equivalent ways to perform the same actions as our examples. Our userspace applications are all designed to go through a TCP interface. By looking at the source for these applications, you can learn our protocol for communicating with the hardware interfaces in any language.

The most common method of development is directly on the SBC. Since debian has space available on the SD card, we include the build-essentials package which comes with everything you need to do C/C++ development on the board.


Vim is a very common editor to use in Linux. While it isn't the most intuitive at a first glance, you can run 'vimtutor' to get a ~30 minute instruction on how to use this editor. Once you get past the initial learning curve it can make you very productive. You can find the vim documentation here.

Emacs is another very common editor. Similar to vim, it is difficult to learn but rewarding in productivity. You can find documentation on emacs here.

Nano while not as commonly used for development is the easiest. It doesn't have as many features to assist in code development, but is much simpler to begin using right away. If you've used 'edit' on Windows/DOS, this will be very familiar. You can find nano documentation here.


We only recommend the gnu compiler collection. There are many other commercial compilers which can also be used, but will not be supported by us. You can install gcc on most boards in Debian by simply running 'apt-get update && apt-get install build-essential'. This will include everything needed for standard development in c/c++.

You can find the gcc documentation here. You can find a simple hello world tutorial for c++ with gcc here.

Build tools

When developing your application typing out the compiler commands with all of your arguments would take forever. The most common way to handle these build systems is using a make file. This lets you define your project sources, libraries, linking, and desired targets. You can read more about makefiles here.

If you are building an application intended to be more portable than on this one system, you can also look into the automake tools which are intended to help make that easier. You can find an introduction to the autotools here.

Cmake is another alternative which generates a makefile. This is generally simpler than using automake, but is not as mature as the automake tools. You can find a tutorial here.


Linux has a few tools which are very helpful for debugging code. The first of which is gdb (part of the gnu compiler collection). This lets you run your code with breakpoints, get backgraces, step forward or backward, and pick apart memory while your application executes. You can find documentation on gdb here.

Strace will allow you to watch how your application interacts with the running kernel which can be useful for diagnostics. You can find the manual page here.

Ltrace will do the same thing with any generic library. You can find the manual page here.

Accessing Hardware Registers

This board implements the NUBS bridge between the CPU and FPGA. The CPU does not implement an SMC bus, because of this the NBUS was created to make an atomic bus using a non-atomic interface. Because it is a non-atomic interface, a locking mechanism using semaphores must be used in order to ensure that two processes do not try to access the NBUS at the same time. When writing applications that communicate over the NBUS you should use the calls in nbus.c and nbus.h. These will compile for c/c++ but if you are using another language such as Java or Python the best implementation is typically to write your hardware accesses in C, and use your languages popen/system() calls to execute the application handling NBUS calls.

All of the registers used in this example code are documented in the Syscon.

Example NBUS application

/* When compiling use the following gcc command:
 * gcc -oexample example.c nbus.c -mcpu=arm9
 * nbus.c and nbus.h must be in the same folder where the gcc command is being run from
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include "nbus.h"

int main (int argc, char **argv)
	uint16_t val;
	int i;

	/* Set DIO 7 low
	 * Set output value to 0
	val = nbuspeek16(0xa);
	nbuspoke16(0xa, val & ~(1 << 7));
	// Set dio 7 direction to output
	val = nbuspeek16(0xc);
	nbuspoke16(0xc, val | (1 << 7));

	/* Set DIO 7 high
	 * DDR is already set to output, so
	 * set output value
	val = nbuspeek16(0xa);

	nbuspoke16(0xa, val | (1 << 7));

	// Toggle Red LED 10 times
	val = nbuspeek16(0x2);

	/* The NBUS lock should be held as little as possible
	 * since other peripherals will need access.  When 
	 * going into an operation like a sleep, a flush, or
	 * any other syscal that will stall the system without
	 * actually needing the lock, it should be released first.
        printf("Starting loop\n");

	for(i = 0; i < 10; i++) {
		if(i % 2) {
			nbuspoke16(0x2, val & ~(1 << 14));
		} else {
			nbuspoke16(0x2, val | ( 1 << 14));

                /* nbuspreempt() can be used to check if there
                 * are other processes waiting to use the bus. If there
                 * are, then the bus is unlocked, given to other processes
                 * and then the bus is re-locked.  When nbuspreempt()
                 * returns the calling process will have the lock again

	return 0;

Another NBUS example can be found in dio.c, this also requires the nbus.c and nbus.h files in order to compile.

Cross Compiling

While it is recommend to develop entirely on the SBC itself, it is also possible to develop from an x86 compatible Linux system using a cross compiler. For this SBC use the cross compiler located here. The resulting binary will be for ARM.

[user@localhost]$ /path/to/arm-fsl-linux-gnueabi/bin/arm-linux-gcc hello.c -o hello
[user@localhost]$ file hello
hello: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

This is one of the simplest examples. For working with a larger project a Makefile will typically be used. More information about Makefiles is available here. Another common requirement is linking to third party libraries provided by Debian on the SBC. There is no exact set of steps for every project when cross compiling, but the process will be very much the same. Provide the cross compiler with access to the necessary headers, libraries, and source files, and install the binary on the target. The following example will link to sqlite from Debian.

Install the sqlite library and header on the SBC:

apt-get update && apt-get install -y libsqlite3-0 libsqlite-dev

This will fetch the binaries from the internet and install them on the SBC. The installed files can then be listed with dpkg:

dpkg -L libsqlite3-0 libsqlite3-dev

The needed files from this output will be the .h and .so files, they will need to be copied to the project directory on the cross-compling host.

See the example with libsqlite3 below. This is not intended to provide any functionality, but just call functions provided by sqlite.

#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

int main(int argc, char **argv)
	sqlite3 *db;
	char *zErrMsg = 0;
	int rc;
	printf("opening test.db\n");
	rc = sqlite3_open("test.db", &db);
		fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
		fprintf(stderr, "SQL error: %s\n", zErrMsg);
	printf("closing test.db\n");
	return 0;

To build this with the external libraries the makefile below can be used. This will have to be adjusted for the proper toolchain path. In this example, the headers are located in external/include and the library in external/lib.

CFLAGS=-c -Wall

all: sqlitetest

sqlitetest: sqlitetest.o
        $(CC) sqlitetest.o external/lib/libsqlite3.so.0 -o sqlitetest
sqlitetest.o: sqlitetest.c
        $(CC) $(CFLAGS) sqlitetest.c -Iexternal/include/

        rm -rf *o sqlitetest.o sqlitetest

The resulting binary can be copied to the target and executed. There are many ways to transfer the compiled binaries to the board. Using a network filesystem such as sshfs or NFS will be the simplest to use if needed frequently during development, but will require a setup. See the host linux distribution's manual for more details. The simplest network method is using ssh/sftp. If running Windows, winscp can be used, or just scp in linux. Make sure a password is set for a user account, root or otherwise, in order to properly ssh or scp files to the target. From winscp, enter the ip address of the SBC, the root username, and the password; this will create an explorer window that can use drag-and-drop of files to copy them to the target.

For scp in linux, run:

#replace with the binary name and the SBC IP address
scp sqlitetest root@

After transferring the file to the board, execute it:

ts:~# ./sqlitetest 
opening test.db
closing test.db

Compile the Kernel

For adding new support to the kernel, or recompiling with more specific options, the kernel can be customized and re-built. An x86 compatible Linux workstation that can handle cross compilation is required. We recommend using a Debian distribution. Compiling the kernel on the device is not supported or recommended. Before building the kernel, the necessary support libraries will need to be installed on the Linux workstation:



yum install ncurses-devel ncurses
yum groupinstall "Development Tools" "Development Libraries"


sudo apt-get install build-essential libncurses5-dev libncursesw5-dev git

If using a 64-bit system, then 32-bit compatibility libraries will be required for the toolchain, for newer Debian and Ubuntu distributions with Multiarch support, use the command:

sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libc6-dev:i386 zlib1g-dev:i386

On older distributions:

sudo apt-get install ia32-libs

For other distributions, please refer to their documentation to find equivalent tools.

Download sources and configure

git clone https://github.com/embeddedTS/linux-

# This sets up the default configuration that we ship with
make ts7600_defconfig
ln -sf initramfs.cpio-ts7600 initramfs.cpio

Once the configuration is loaded, any needed changes can be made to it. A common reason for recompiling is to add support that was not built into the standard image's kernel. An ncurses menu to browse available configuration options can be opened with:

make menuconfig

The "/" key is to search for specific terms through the kernel.

Build the kernel

Once any customization is completed, the kernel can be built. This usually takes about 5-10 minutes depending on workstation CPU speeds:


Build bootstream

The i.MX28 utilizes what NXP calls a bootstream This is a series of bootlets that are all put together in a binary blob that make up a bootloader for the whole system. The in-CPU ROM bootloader is very small and therefore uses the bootstream on the boot media to handle further loading. The default bootstream sets up RAM, power, and contains the kernel to be run. Every time the kernel is built, a new bootstream must be compiled containing the new kernel image. The following script is used to take the newly built kernel and output a bootstream for an SPI device as well as an SD card:


This will create two files, imx-bootlets-src-10.12.01/imx28_ivt_linux.sb and imx-bootlets-src-10.12.01/imx28_ivt_linux.spi. The imx28_ivt_linux.sb file is the standard image used for the SD card, while the imx28_ivt_linux.spi is meant to be imaged to the SPI flash device on the TS-7600.

Building External Wireless Modules

In order to support the wide range of USB WiFi modules that Technologic Systems has offered over the years, the compat-wireless project is used to build all compatible modules. A simple command is used to build them:


Install the bootstream (kernel/initramfs) and Modules

Next, install the kernel and modules to the SD card. NXP uses a specialized booting mechanism for their processor, so to simplify installation we provide two scripts to handle installation of the kernel+bootstream, kernel modules, headers, and compat-wireless modules.

For example, if your workstation's SD card is /dev/mmcblk0:

./install_bootstream imx-bootlets-src-10.12.01/imx28_ivt_linux.sb mmcblk0 p1
./install_hdr_mod mmcblk0p2

Note: On newer Linux distributions, the output of 'fdisk' has changed. If the unit fails to boot after a compile, take a look at the output of the './install_bootstream ... ' command. If the line
./install_bootstream: line 122: [: !=: unary operator expected

is printed, then a patch must be applied to address this issue. Use the following command to apply the patch:

patch -p1 < install_bootstream-newer-fdisk.patch

Install the bootstream (kernel/initramfs) to SPI flash

The ./build_bootstream command creates the file imx-bootlets-src-10.12.01/imx28_ivt_linux.spi that can be used to program the SPI flash, see the SPI Flash section for more information. The ./install_hdr_mod command copies both the SPI bootstream and the stock bootstream to /lib/modules so they may be used to program SPI devices or NAND.

This bootstream is the exact same kernel/initramfs that is made for the SD card, it has the exact same init script.

Linux 3.14

3.14 kernel support has been brought to the TS-7600. All of the software remains the same except for two notes of interest as pointed out below. There is a small update to the initramfs to accommodate the newer kernel, however only the kernel compile process as outlined below needs to be followed in order to update those as needed.

There are two notes that should be heeded for updating to 3.14:

  • The RTC now has a kernel interface. It is a standard /dev/rtc device as presented by the kernel. There is no need to use 'tshwctl --getrtc' or 'tshwctl --setrtc' however they can still be used. The use of 'tshwctl --nvram' is unaffected and should still be used in the same manner.
  • The WDT is not controlled by the kernel, nor can the kernel safely communicate with the FPGA, and under 3.14 "halt" is a different state than in 2.6. All of this results in the WDT feeder thread no longer running after shutdown is complete, which means that after the current WDT timer expires, the TS-7600 will reset. The workaround for this is to issue a WDT command before halting: 'echo 3 > /dev/watchdog && shutdown -h now'

For adding new support to the kernel, or recompiling with more specific options, an x86 compatible Linux host that can handle the cross compiling will be needed. Compiling the kernel on the device is not supported or recommended. Some support libraries are required on the workstation before a kernel can be built:


All systems:

Be sure to Create an SD card with the latest image available before running through this process.

Note that some of the scripts below use pre-compiled binaries for x86 Linux. If compiling on another platform, including x86_64, there may be compatibility issues. Be sure to install compat libraries if applicable.

Download and unpack the cross compiler

wget ftp://ftp.embeddedTS.com/ts-arm-sbc/ts-7680-linux/cross-toolchains/imx28-cross-glibc.tar.bz2
tar xvf imx28-cross-glibc.tar.bz2 -C /path/to/folder/

/path/to/folder can be any directory so long as the current user has permissions to write to it. Remember this path as its used later during the kernel build procedure.


yum install ncurses-devel ncurses
yum groupinstall "Development Tools" "Development Libraries"


sudo apt-get install build-essential libncurses5-dev libncursesw5-dev git

If building on a 64-bit system, then 32-bit libraries will be required for the toolchain. For newer Debian and Ubuntu distrubutions with Multiarch support, use the command:

sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libc6-dev:i386 zlib1g-dev:i386 libstdc++6:i386

On older distributions:

sudo apt-get install ia32-libs

For other distributions, please refer to their documentation to find equivalent tools.

Download sources and configure

git clone https://github.com/embeddedTS/linux-3.14.28-imx28.git
cd linux-3.14.28-imx28/

# Switch to the branch specific for this SBC
git checkout 7600-4600

# Note that there is another compatible branch that has community applied patches.
# This includes an update to kernel version 3.14.79, as well as AUFS support.
# AUFS is a unification filesystem, similar to UnionFS.
# By setting soft jumper 4 when using this branch, the root filsystem is unified with a RAM based tmpfs.
git checkout linux-3.14.79-7600-4600

# These next commands set up some necessary environment variables
export ARCH=arm
export CROSS_COMPILE=/path/to/folder/arm-fsl-linux-gnueabi/bin/arm-linux-
export LOADADDR=0x40008000

# This sets up the default configuration
make ts7600_defconfig

Once the default configuration is set up, changes can be made to it before building. A common reason for recompiling is to add support that was not built into the standard image's kernel. An interactive menu of options is available by running:

make menuconfig

Use the "/" key to search for specific terms through the kernel.

Build the kernel

Once configured, the kernel can be built. Since Linux 3.14 uses Device Tree, and the NXP bootloader is not set up to handle that, an additional preparation step is needed as well to append the binary device tree to the end of the kernel binary.

make && make zImage && make modules

We recommend running 'make' with the -jX argument, where X is the number of CPU cores+1 present on the build machine. This will greatly increase build speed.

Next, the correct DTB needs to be appended to the zImage binary. However, because of the way the NXP bootloader detects and passes RAM, it is not compatible with the kernel Device Tree. Therefore, there are two DTB files, one to specify 128 MB of RAM, and the other for 256 MB. The 128 MB variant will boot on both, however on a system with an actual 256 MB of RAM, only 128 MB of that will be accessible. On the other hand, the 256 MB DTB is only compatible with a TS-7600 that has 256 MB of RAM; it will not boot on a unit with 128 MB.

cat arch/arm/boot/zImage arch/arm/boot/dts/imx28-ts7600-128M.dtb > zImage


cat arch/arm/boot/zImage arch/arm/boot/dts/imx28-ts7600-256M.dtb > zImage

And now, move the zImage back to its expected location:

mv zImage arch/arm/boot/

Build bootstream

The i.MX28 utilizes what NXP calls a "bootstream," this is a series of "bootlets" that are all put together in a binary blob. The default bootstream sets up RAM, power, and contains the kernel to be run. Every time a kernel is rebuilt, a new bootstream must be compiled containing the new kernel image. The following script is used to take the newly built kernel and output a bootstream for the SD card:


This will create imx-bootlets-src-10.12.01/imx28_ivt_linux.sb The .sb file is the standard image used for the SD card.

Install the bootstream (kernel/initramfs), headers, and modules

Next, install the kernel and modules to the SD card. We provide a simple script to copy the kernel uImage file, kernel modules, and headers to the SD card to update everything at once.

For example, if your workstation's SD card is /dev/mmcblk0:

./install_bootstream imx-bootlets-src-10.12.01/imx28_ivt_linux.sb mmcblk0 p1
./install_hdr_mod mmcblk0p2

If your workstation's SD card is /dev/sdc:

./install_bootstream imx-bootlets-src-10.12.01/imx28_ivt_linux.sb sdc 1
./install_hdr_mod sdc2

Note: On newer linux distributions, the output of 'fdisk' has changed. If the unit fails to boot after a compile, take a look at the output of the './install_bootstream ... ' command. If the line
./install_bootstream: line 122: [: !=: unary operator expected

is printed, then a patch needs to be applied to fix this. Use the following command to apply the patch:

patch -p1 < install_bootstream-newer-fdisk.patch

Using the Oracle JRE

Oracle provides a headless JRE binary for the ARMv5 processor series which is compatible with this processor. In many cases the OpenJDK JRE is sufficient for an application, but Oracle's JRE provides better performance. To install this JRE, first accept the license and download this from Oracle here.

Your version number may be slightly different, but the process should remain the same:

tar -xf ejre-7u45-fcs-b15-linux-arm-sflt-headless-26_sep_2013.tar.gz
mv ejre1.7.0_45/ /usr/share/oracle-jre/
ln -s /usr/share/oracle-jre/bin/java /usr/bin/java

You can verify this is installed by checking the version:

root@ts:~# java -version
java version "1.7.0_45"
Java(TM) SE Embedded Runtime Environment (build 1.7.0_45-b15, headless)
Java HotSpot(TM) Embedded Client VM (build 24.45-b08, mixed mode)



The TS-7600 i.MX28 CPU brings out 5 channels of LRADC, Low-Resolution Analog to Digital Converters. The CPU peripheral is 12-bit, 1.85 V input ADC with a 1.3% absolute error at approximately 428 KHz. The channels do have diode clamps in place for added over-volt protection. Four of the channels are brought out to DIO pins and are in parallel with the signal lines. These are fed through a divide-by-two resistor divider, and then are connected to the ADC input pins. The fifth channel is hard-wired to the 5v power input through a divide-by-three resistor network in order to monitor the input voltage.

In order to get accurate readings from the four channels brought out, the DIOs in parallel must be tri-stated and have no other signals driving. The DIO pins in parallel do have pull-up resistors enabled inside the FPGA which can skew the ADC results, and requires that the ADC signal be strong enough to overcome the pull-ups. There are two actions that can be taken to reduce the skew of ADC results from the FPGA pull-ups. One is to calibrate out the error. If the ADC input signal is constant current, the voltage shift induced by the FPGA will be constant as well. This error can then be calibrated out very easily. The other option is to modify the FPGA and remove the pull-up on the associated DIO pins, see the FPGA Programming section for more information. Please note that removing the pull-ups on the DIO pins may have adverse side effects since the pins will be floating. The resistor divider on the ADC pins will help to create a very weak pull-down however. See the DIO section for pin placement.

Sample code to read the ADCs is provided by Technologic Systems, see imx28_adc.c. This code can be used as-is, or integrated in to a C application. The code can also be translated in to other languages that allow for direct memory mapping and manipulation. The sample code will output the result of all 5 channels in millivolts after sampling each channel 10 times and averaging the results. This sample code also allows for easily adjusting offset errors via calibration.

This sample code is integrated in to our 'tshwctl' application for quick testing and verification:

tshwctl --cpuadc

The command will return output similar to the following:


For more information about the LRADCs, see the CPU manual

Battery Backed RTC and Temperature Sensor

This board includes a temperature compensating RTC which maintains ±5 ppm between 0C to +85C. This is accessed in software using tshwctl. By default, tshwctl will run "tshwctl --getrtc" on startup which will pull system time from the RTC, and set the system time. During the Technologic Systems production process the RTC will be programmed with an accurate time.

If time ever needs to be set you can run:

tshwctl --setrtc

This will take the system time and write it to the RTC. The battery in the RTC will last approximately 10 years for most applications, but the RTC allows you to see when the battery reaches low or critical voltages:

# tshwctl --rtcinfo             

rtcinfo_oscillator_ok is true when the RTC is operational and time is being kept
rtcinfo_batt_low is true when the battery is less than 2.805v (85% of 3.3v)
rtcinfo_batt_crit is true when the battery is less than 2.475v (75% of 3.3v)

Note: While the RTC will remain operational with a battery voltage down to 1.8v, the lithium battery used has a very steep discharge curve. Once the battery reaches critical level it should be replaced.

rtcinfo_first/lastpoweroff/on are two registers that denote the first time the RTC started using battery power, and the last time power was restored and the RTC stopped using battery power for timekeeping. The output of these registers is in the format MMDDhhmmss. Once `tshwctl --rtcinfo` is called, these registers are cleared and able to be set again. This is a great tool to check if a power off has occurred and how long it lasted.


Note: CAN is not available on TS-7600 Rev. A PCBs, only Rev. B and beyond support the CAN interface. In addition to this, FPGA Rev. 4 or greater is required. See the PCB Revisions and FPGA Changelog for more information.

In order to utilize CAN on the TS-7600, the DIO pins that the CAN signals reside on must be put in to CAN mode. This is done by setting bit 10 of register 0x2 in the Syscon:

tshwctl -a 0x2 -r
#Returns 0x8904
tshwctl -a 0x2 -w 0x8d04

The CPU brings out one CAN port compatible with the linux SocketCAN implementation. The ports can be set up and used with the following command:

ifconfig can1 up

In order to set the baud rate of the CAN interface, the interface must first be brought down with:

ifconfig can1 down

At this point, the desired baud rate can be directly entered in to the file "/sys/devices/platform/FlexCAN.1/bitrate". For example, to set a baud rate of 750kHz on the interface:

ifconfig can1 down
echo 750000 > /sys/devices/platform/FlexCAN.1/bitrate
ifconfig can1 up

At this point the ports can be used with standard SocketCAN libraries. In debian we provide cansend and candump to test the ports or as a simple packet send/recv tool. The following are some simple commands that can be used:

candump can1
cansend can1 7Df#03010c

Note: It has been observed that the flexCAN driver present in the 2.6.35 kernel has some issues. If your application is running in to these issues, please see the discussion thread here: https://community.nxp.com/thread/272930 Apply the mentioned patch and compile the kernel. The patch is also included in our kernel git repo. Be sure after the patch is applied to set the kernel config options CONFIG_CAN_DEV, CONFIG_CAN_CALC_BITTIMING, and CONFIG_CAN_FLEXCAN. They can be modules or built-in.

The above example packet is designed to work with the Ozen Elektronik myOByDic 1610 ECU simulator to read the RPM speed. In this case, the ECU simulator would return data from candump with:

 <0x7e8> [8] 04 41 0c 60 40 00 00 00 
 <0x7e9> [8] 04 41 0c 60 40 00 00 00 

In the output above, columns 6 and 7 are the current RPM value. This shows a simple way to prove out the communication before moving to another language.

The following example sends the same packet and parses the same response in C:

#include <stdio.h>
#include <pthread.h>
#include <net/if.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <assert.h>
#include <linux/can.h>
#include <linux/can/raw.h>

int main(void)
	int s;
	int nbytes;
	struct sockaddr_can addr;
	struct can_frame frame;
	struct ifreq ifr;
	struct iovec iov;
	struct msghdr msg;
	char ctrlmsg[CMSG_SPACE(sizeof(struct timeval)) + CMSG_SPACE(sizeof(__u32))];
	char *ifname = "can0";
	if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
		perror("Error while opening socket");
		return -1;
	strcpy(ifr.ifr_name, ifname);
	ioctl(s, SIOCGIFINDEX, &ifr);
	addr.can_family  = AF_CAN;
	addr.can_ifindex = ifr.ifr_ifindex;
	if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		return -2;
 	/* For the ozen myOByDic 1610 this requests the RPM guage */
	frame.can_id  = 0x7df;
	frame.can_dlc = 3;
	frame.data[0] = 3;
	frame.data[1] = 1;
	frame.data[2] = 0x0c;
	nbytes = write(s, &frame, sizeof(struct can_frame));
	if(nbytes < 0) {
		return -3;

	iov.iov_base = &frame;
	msg.msg_name = &addr;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = &ctrlmsg;
	iov.iov_len = sizeof(frame);
	msg.msg_namelen = sizeof(struct sockaddr_can);
	msg.msg_controllen = sizeof(ctrlmsg);  
	msg.msg_flags = 0;

	do {
		nbytes = recvmsg(s, &msg, 0);
		if (nbytes < 0) {
			return -4;

		if (nbytes < (int)sizeof(struct can_frame)) {
			fprintf(stderr, "read: incomplete CAN frame\n");
	} while(nbytes == 0);

	if(frame.data[0] == 0x4)
		printf("RPM at %d of 255\n", frame.data[3]);
	return 0;

See the Kernel's CAN documentation here. Other languages have bindings to access CAN such as Python, Java using JNI.

In production use of CAN we also recommend setting a restart-ms for each active CAN port.

ip link set can0 type can restart-ms 100

This allows the CAN bus to automatically recover in the event of a bus-off condition.


This board features the i.MX286 454 MHz ARM9 from NXP. For more information about the processor and it's included peripherals, refer to the CPU manual.

CPU Frequency

The i.MX28 CPU can run at multiple frequencies. By default the CPU runs at maximum speed, 454 MHz, but can be lowered for power savings.

See current speed:

cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq
454736   # Default speed

Set speed to lowest:

echo 261818 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed

Set speed to highest:

echo 454736 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed


The TS-7600 CPU and FPGA provide real-world DIO. The CPU DIO typically have up to 4 functions associated with various pins (I2C, PWM, SPI, etc); see the CPU manual CPU manual for the complete listing and for information on how to control these DIO. This section only lists FPGA DIO.

All FPGA DIO are controlled by three distinct register types: Direction, Input Data, and Output Data. To use any DIO pin, the direction register must be set (0 for input, 1 for output), then either the input register may be read, or the output register may be written to. These registers are described in the Syscon section.
For example, to write to DIO_0, the corresponding bit in the Direction register needs to be set in order to make the pin an output. At this point, the pin will reflect the state of the corresponding bit in the Output register, with a 12mA drive strength. Under most circumstances, the Input register will reflect the Output register if the DIO is set to an output. If the pin is overdriven however, the Input register may read a different value. Clearing a corresponding bit in the Direction register will turn the DIO pin in to a high impedance input with a weak pullup internal to the FPGA, this pin state can be read from the Input register.

The TS-7600 FPGA also contains our EVGPIO core, event driven GPIO. This allows for atomic setting of DIO pins (no need to read-modify-write whole registers) as well as setting up pins to generate interrupts on state changes. See the EVGPIO and Interrupts sections for more information.

The DIO of the TS-7600 are numbered to correspond with pin numbers of both the 44-pin header and the 26-pin header, there are 70 pins total with 54 usable as DIO pins. The pin numbers in the table below count from 1 to 44 on the 44-pin header, and 45 to 70 on the 26-pin header.

All 69 of the DIO from the FPGA will default to the DIO mode. These pins coming from the FPGA are all 3.3V tolerant. To manipulate these DIO you can access the Syscon, either through tshwctl or a custom application. DIO pins that are not listed do not have an FPGA DIO connected to them, see the 44-pin header and the 26-pin header sections to evaluate what a specific pin is designated as.

DIO # Pin # Alternate Function
5 44_5 MODE2, XUART0 TX
6 44_6 XUART0 RX
9 44_9 External Reset
11 44_11 SPI CS#
12 44_12 SPI MISO
13 44_13 SPI MOSI
14 44_14 SPI CLK
19 44_19 XUART1 TX
20 44_20 XUART1 RX
21 44_21 XUART2 TX
22 44_22 XUART2 RX
23 44_23 XUART3 TX
24 44_24 XUART3 RX
25 44_25 XUART4 TX
26 44_26 XUART4 RX
27 44_27 XUART1 TXEN
28 44_28 XUART2 TXEN
29 44_29 XUART5 TXEN
30 44_30 XUART6 TXEN
31 44_31 XUART5 TX
32 44_32 XUART5 TX
33 44_33 XUART6 TX
34 44_34 XUART6 RX
35 44_35 XUART7 TX
36 44_36 XUART7 RX, LRADC1
37 44_37 XUART0 TXEN, LRADC2
38 44_38 XUART3 TXEN, LRADC3
39 44_39 XUART4 TXEN, LRADC4
40 44_40 XUART7 TXEN
41 26_1 XUART0 CTS#
42 26_2 XUART1 CTS#
43 26_3 XUART2 CTS#
44 26_4 XUART3 CTS#
45 26_5 XUART4 CTS#
46 26_6 XUART5 CTS#
47 26_7 XUART6 CTS#
48 26_8 XUART7 CTS#
49 26_9 N/A
50 26_10 N/A
51 26_11 N/A
52 26_12 N/A
53 26_13 N/A
54 26_14 N/A
55 26_15 N/A
56 26_16 N/A
57 26_17 N/A
58 26_18 N/A
59 26_19 N/A
60 26_20 N/A
61 26_21 N/A
62 26_22 N/A
64 26_24 N/A
65 26_25 N/A
66 26_26 N/A
68 N/A Eth Amber LED [1]
69 N/A USB power [2]
  1. When Direction is set, LED reflects state of Output reg. When Direction is clear, LED reflects LINK light from Ethernet PHY
  2. When Direction is set, Output bit controls power to the USB host ports


This board features the EVGPIO core (Event Driven GPIO) which allows a low bandwidth mechanism to monitor all FPGA DIO on a shared interrupt. All DIO are accessed atomically through two registers. The Data/IRQ En. register is used to read DIO state changes, set output values, and enable IRQ on DIO state changes. The Data Direction Register is used to set a DIO to an input or output. The Data/IRQ En. Register will only return data on reads when the IRQ En. bit is set on a DIO Number, a DIO pin has changed state since the IRQ En. was set, and all previous state changes of DIO have been read. The Data Direction Register will never read back anything other than 0x0.

Once the EVGPIO core senses a state change it will wait to be read before updating other pin states. What this means is, DIO_X changes state and the EVGPIO core generates an interrupt; if DIO_Y changes state and then reverts back before the first DIO_X change is read, then the DIO_Y state change is never seen. That being said, if DIO_X changes state, then DIO_Y changes state once, and DIO_X changes back before the first DIO_X state change is read, all three events will be reported by the EVGPIO core. At most, the core will retain two state changes per DIO. Pin states are only updated while the EVGPIO core is idling waiting for a state change, or once a reported state change is read. Once a state change has been read for a particular DIO, then the EVGPIO core can queue up another change. If however a particular DIO cycles multiple times before it is read, the number of times it changed state will be lost and the EVGPIO core will return at most 2 state changes. Because of this it, is beneficial to use the userspace IRQ examples or evgpioctl to watch pin states and read them quickly to an end application. See the Interrupts section for more information on IRQ latency with userspace IRQs and the NBUS.

The EVGPIO data and mask registers can be used directly in your application. Setting a pin direction, output value, and reading input changes are accessed through the EVGPIO data register.

Using Number 0 to 69 will set Value to DIO_Number. Using Number 70 to 127 will set IRQ Enable for DIO_(Number - 70). Note that this scheme will only allow interrupts on DIO 0 to 57. See Interrupts for more information on using these interrupts, and see Syscon for information on where this EVGPIO core is located in address space.

EVGPIO Data/IRQ En. Register
Bits Description
15:9 Reserved (Write 0)
8 Valid Read Data [1]
7 Value
6:0 DIO/Mask Number
  1. When writing, write 0. During a read this indicates if this read includes new valid changes. After an interrupt this register should be read until this bit returns 0.

The second register is Data Direction Register. DIO Number is set to an Output when bit 7 is set, and set to an Input when bit7 is cleared.

EVGPIO Data Direction Register
Bits Description
15:8 Reserved (Write 0)
7 Output/Input
6:0 DIO number


MicroSD.png Click to download the latest 4GB DoubleStore SD card image.

This series supports DoubleStore which can be used to significantly increase the reliability of SD cards. This allows one SD image to be written to two cards allowing redundancy among both SD cards. See our white paper for more information on the concept. Development can take place with a single MicroSD card, but for using DoubleStore 2 MicroSD cards are used.

Note: Due to the design of the SBC and its use of the NXP bootstream, the device cannot directly boot from a DoubleStore dataset. The device must first boot from on-board media (SPI, NAND, eMMC), and the initramfs will then find the DoubleStore dataset and mount it to the Debian directory. From there, the unit can boot to Debian on the DoubleStore dataset, either manually with the 'exit' command, or by setting up the initramfs to boot straight to Debian. See the Initramfs section for more details on setting up the initramfs to accomplish this.

The default SD image is 3GB which is designed to fit in a dual-card Doublestore configuration. When dual card doublestore is used it stores the same image on both cards and also includes metadata and checksums for the entire image.

You can use the dblstorctl utility to work with DoubleStore on your Linux workstation. The simplest way to get doublestore set up is to first take a backup of your SD image, and then use dblstorctl on a workstation to convert it:

export INPUTIMAGE="yourimagebackup.dd"
eval $(stat -c "imgsize=%s" $INPUTIMAGE)
dblstorctl --primary ${INPUTIMAGE}.dblstor --fallback /dev/null --init --writeimg "$INPUTIMAGE" --size=${imgsize}B

This will output yourimagebackup.dd.dblstor which can be written directly to both SD cards:

dd if=yourimagebackup.dd.dblstor bs=4M conv=fsync of=/dev/sdb # replace sdb with your SD card device

Note that the stock DoubleStore image linked at the top of this section can be used in place of 'yourimagebackup.dd' to write our stock image to a two card DoubleStore set.

The board will boot the same using the DoubleStore MicroSD cards, but dblstorctl includes additional information:

# dblstorctl --stats
fallback_configuration="separate disk"

fallback_configuration should read "seperate disk" when booting doublestore correctly. For diagnostics, the tainted and failed settings are the most relevant:


When a card is tainted, the LED near the card will begin to blink. This indicates Doublestore has seen the card perform an unexpected behavior that DoubleStore was able to correct.

Ethernet Port

The CPU implements a 10/100 Ethernet controller with support built into the Linux kernel. Standard Linux utilities such as ifconfig and ip can be used to control this interface. See the Configuring the Network section for more detail. For the specifics of this interface see the CPU manual.

External Reset

Driving the external reset pin (DIO 9) low will reset the CPU by default. You can disable this functionality by running:

tshwctl --resetswitchoff


The TS-7600 features an FPGA designed to accentuate the i.MX28 CPU peripherals with some additional peripherals and flexibility. The FPGA is connected to the CPU through our NBUS interface, which is a 16-bit data bus interface. There are no 8bit bus cycles to the FPGA.

To access peripherals in the FPGA, use the following base addresses:

Offset Usage
0x00 Syscon registers
0x40 XUART Interface registers
0x5c XUART Memory window interface
0x60 SPI interface

FPGA Bitstreams

The FPGA has the capability to be reloaded on startup and reprogram itself with different configurations. The default bitstream is hardcoded into the FPGA, but the soft reloaded bitstreams can be placed in /ts/ts<model>-fpga.vme.bz2 on the Debian root to make the board load the bitstream on startup. If we do not have a configuration you need, you can build a new bitstream, or contact us for our engineering services.

Bitstream XUARTs SPI
Default (5K LUT) 0-7 On

You can update to the latest FPGA by booting to Debian and running:

cd /ts/
wget ftp://ftp.embeddedTS.com/ts-arm-sbc/ts-7600-linux/binaries/ts-bitstreams/ts7600-fpga.vme.bz2

The FPGA is loaded in to the FPGA SRAM on every poweron, so this file will need to exist for all future boots.

An FPGA revision changelog can be found in the Revisions and Changes section.

FPGA Programming

Note: We do not provide support for the opencores under our free support, however we do offer custom FPGA programming services. If interested please contact us.

We provide an open version of the Verilog project that contains the functionality of the default FPGA bitstream. The FPGA bitstream is built using Lattice Diamond which is free and runs under Windows or Linux (Redhat). This allows you to modify the verilog and create a jedec file with your custom logic. The jedec is converted to a vme file which is loaded from the SD card and used to reprogram the SRAM of the FPGA on every startup. This requires approximately a second during startup to reprogram, but allows you to recover by removing the bitstream file from the SD card in the case of a faulty bitstream.

The opencore FPGA sources are available here. Custom logic can be built by implementing a wishbone compatible core, or by extending the cores we already have connected.

The ts7600_top.v file is used to connect all of the wishbone cores, and map any DIO. The syscon.v is used for most common system configuration registers. As a simple example these next steps will modify the submodel ID located at 0x02 in the syscon.v.

Open up the Lattice diamond tools and open the .ldf file to open the project. On the bottom left there are 3 tabs to control the left panel (Files, Process, and Hierarchy). Go to Files, and double click syscon.v. At line 131 is:

localparam model = 16'h7600;
localparam submodel = 4'h0;
localparam revision = 4'h1;

You can edit the custom value to:

localparam submodel = 4'h1;

The custom register is not used by any default software and is a safe register to use for a custom version number. Do not change the model number or the revision number, many of our initramfs scripts rely on this to identify the current board. The default bitstream will always use 0 for the submodel value.

Save the file and go to the "Process" tab. Double click "Place & Route Trace" to begin synthesizing the bitstream. This will take approximately 5-10 minutes. Once this is finished open the "Reports" tab from the top open file list. Under "Analysis Reports" click on "Place & Route Trace". This is used to verify timing of your build. Under "Preference Summary" make sure none of the clock domains list errors. If timing is not met this will cause seemingly random issues with the bitstream which will usually present first as SD corruption.

Once the timing has been verified, double click "JECEC File" on the "Process" tab to build the jed file. Once this is finished there will be a "ts4710_default.jed" in the project folder. In order for the board to use this it must be converted to a vme file. This is generated using "jed2vme":

jed2vme ts7600_default.jed | bzip2 > ts7600-fpga.vme.bz2
WARNING: Generating a VME using Lattice's tools can generate a flash bitstream which WILL render your board unbootable.

Once this is built it should be placed on the second partition of the SD card as "/ts/ts<model>-fpga.vme.bz2" This should match the device's model such as "/ts/ts7600-fpga.vme.bz2".

A bitstream can be tested at any time by loading it with the following command:

tshwctl --loadfpga ts7600-fpga.vme.bz2

Once it is loaded on the SD card the board can be booted normally. The green and red LEDs will shut off during programming, and then turn back on after the bitstream has been reloaded. During the time it is programming, the NBUS lock is held and no other applications will attempt to issue a bus cycle while the FPGA is not completely up and running. Once it has reloaded you can use tshwctl to verify the register has changed:

tshwctl -i

On the default bitstream this will return submodel=0x0, with the modified version above it will read submodel=0x1


A standard two-wire I2C interface is provided on this SBC. The i.MX28 CPU has I2C hardware to communicate with devices on the bus. The hardware is able to be accessed from userspace with the linux i2c-dev interface. On this SBC the I2C pins from the CPU are connected to the on-board RTC, and then brought out to external pins. See the External Interfaces section for the location of these signals.

The RTC on the SBC uses two different addresses, one for the actual RTC registers, the other for the RTC's onboard NVRAM.

Address Function
0x6F RTC
0x57 NVRAM

Outside of those addresses, no other I2C addresses are in use on this SBC.

For more information on the i.MX28 I2C implementation, see the CPU manual.


We include a userspace IRQ patch in our kernels. This allows you to receive interrupts from your applications where you would normally have to write a kernel driver. This works by creating a file for each interrupt in '/proc/irq/<irqnum>/irq'. The new irq file allows you to block on a read on the file until an interrupt fires.

The original patch is documented here.

The Linux kernel supports 3 IRQs from the FPGA. Because of the nature of the NBUS, this requires three separate lines from the FPGA to the CPU. Currently only two IRQs are used from the CPU, one for XUARTS and one for EVGPIO. The third IRQ is not hooked up by default. Any of the IRQs can be repurposed by customization of the FPGA. At any time, the FPGA can toggle the interrupt line, however in order for the kernel to respond to it, the IRQ must be opened first.

CPU IRQ # Name
155 XUART IRQ (not used by xuartctl --server by default)
228 Unused

This example below will work with any of our products that support userspace IRQs. It opens the IRQ number specified in the first argument, and prints when it detects an IRQ.

#include <stdio.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv)
	char proc_irq[32];
	int ret, irqfd = 0;
	int buf; // Holds irq junk data
	fd_set fds;

	if(argc < 2) {
		printf("Usage: %s <irq number>\n", argv[0]);
		return 1;

	snprintf(proc_irq, sizeof(proc_irq), "/proc/irq/%d/irq", atoi(argv[1]));
	irqfd = open(proc_irq, O_RDONLY| O_NONBLOCK, S_IREAD);

	if(irqfd == -1) {
		printf("Could not open IRQ %s\n", argv[1]);
		return 1;
	while(1) {
		FD_SET(irqfd, &fds); //add the fd to the set
		// See if the IRQ has any data available to read
		ret = select(irqfd + 1, &fds, NULL, NULL, NULL);
		if(FD_ISSET(irqfd, &fds))
			FD_CLR(irqfd, &fds);  //Remove the filedes from set
			printf("IRQ detected\n");
			// Clear the junk data in the IRQ file
			read(irqfd, &buf, sizeof(buf));
		//Sleep, or do any other processing here
	return 0;


On all of our baseboards we include 2 indicator LEDs which are under software control. You can manipulate these using "tshwctl --greenledon --redledon" or "tshwctl --greenledoff --redledoff". The LEDs have 4 behaviors from default software.

Green Behavior Red behavior Meaning
Solid On Off System is booted and running
Solid On On for approximately 15s, then off Once the system has booted the kernel and executed the startup script, it will check for a USB device and then determine if it is a mass storage device. This is used for updates/blasting through USB. Once it determines this is not a mass storage device the red LED will turn back off.
On for 10s, off for 100ms, and repeating Turns on after Green turns off for 300ms, and then turns off for 10s The watchdog is continuously resetting the board. This happens when the system cannot find a valid boot device, or the watchdog is otherwise not being fed. This is normally fed by tshwctl once a valid boot media has started. See the #Watchdog section for more details.
Off Off The FPGA is not able to start. Typically either the board is not being supplied with enough voltage, or the FPGA has been otherwise damaged. If a stable 5 V is being provided and the supply is capable of providing at least 1 A to the device, an RMA is suggested.
Blinking about 5ms on, about 10ms off. Blinking about 5ms on, about 10ms off. The board is receiving too little power, or something is drawing too much current from the device's power rails.

MicroSD Card Interface

The i.MX28 SD card controller is used for both SD cards present on the board which supports the SD and SDHC specifications. This controller has been tested with Sandisk Extreme SD cards which allow read speeds up to 20.5MB/s, and write speeds up to 21.5MB/s.

Our default software image contains 2 partitions:

Device Contents
/dev/mmcblk0 SD Card block device
/dev/mmcblk0p1 Kernel and initramfs
/dev/mmcblk0p2 Full Debian linux partition

NBUS (FPGA to CPU connection)

This CPU uses a NAND bus to access the FPGA registers. Since this is not an atomic access, we have created the NBUS to allow applications to safely share access to FPGA resources.


The RTC has an included 128-byte battery-backed NVRAM which can be accessed using tshwctl. Its contents will remain with the main power off, so long as the RTC battery is installed and withing a valid voltage range.

tshwctl --nvram

This will return a format such as:


This breaks up the NVRAM into 32 32-bit registers which can be accessed in bash. As this uses the name=value output, "eval" can be used for simple parsing:

eval `tshwctl --nvram`
echo $nvram2

From the above value, this would return 0x48ca4278. To set values, the respective environment variable name can be set:

nvram0=0x42 tshwctl --nvram

Note that the command 'tshwctl --nvram' will output the current contents of NVRAM before setting any new values. At this point, running 'tshwctl --nvram' once more will print the updated contents for verification. This can be used for reading a 32-bit quantity and updating it with a single command.

Random Number Generator

Because many embedded systems do not have much entropy, we have included a core in the FPGA with a random number generator. On startup, tshwctl is called with the --setrng option to seed Linux's random number generator from the hardware random number generator. Without a good source of entropy, Linux's random number generator will start up in a very predictable state which is undesirable for the security of many cryptography protocols.


The SPI controller is implemented in the FPGA. This is commonly accessed by accessing the registers directly. This core is found at offset 0x60 in #NBUS space. The core itself is 16bits wide, however in order to accommodate 8bit (or any multiple of 8bit) SPI transactions the data registers will only use the lower 8bits, the upper 8 bits are ignored.

The table below is the register map for the SPI in the FPGA:

Offset Access Bit(s) Description
0x0 Read Only 15 SPI MISO state
Read/Write 14 SPI CLK state
Read/Write 13:10 Speed[3:0] - 0 (highest), 1 (1/2 speed), 2 (1/4 speed)...
Read/Write 9:8 LUN (0-3 representing the 4 chip selects)
Read/Write 7 CS (1 - CS# is asserted)
N/A 6:1 Reserved
Read/Write 0 Speed[4]
0x2 Read Only 15:0 Previous SPI read data from last write
0x4 N/A 15:0 Reserved
0x6 N/A 15:0 Reserved
0x8 Read/Write 15:0 SPI read/write with CS# to stay asserted
0xa Read Only 15:0 SPI pipelined read with CS# to stay asserted
0xc Read/Write 15:0 SPI Read/Write with CS# to deassert post-op
0xe N/A 15:0 Reserved

The SPI clk state register should be set when CS# is deasserted. Value 0 makes SPI rising edge (CPOL=0), 1 is falling edge (CPOL=1). This only applies to speed >= 1.

The clock feeding the SPI peripheral is 75MHz, speed settings break down as follows:

Value Speed
0 Do Not Use
1 37.5MHz
2 18.75MHz
3 12.5MHz
4 9.375MHz
5 7.5MHz
6 6.25MHz
7 5.36MHz
8 4.68MHz
9 4.17MHz
15 2.5MHz
19 1.97MHz
31 1.21MHz

The pipelined read register is for read bursts and will automatically start a subsequent SPI read upon completion of the requested SPI read. Reading from this register infers that another read will shortly follow and allows this SPI controller "a head start" on the next read for optimum read performance. This register should be accessed as long as there will be at least one more SPI read with CS# asserted to take place.


All of the registers below are 16bits wide and must be accessed through the NBUS. This can either be done directly via tshwctl to read/write the registers, or using some of the options to tshwctl to change them. Another method is to manipulate the registers via C code using our NBUS API, see the Hardware Registers section for more information.

Offset Bits Usage
0x0 15:0 Model ID: Reads 0x7600
0x2 15 Green LED (1 - on)
14 Red LED (1 - on)
13-12 Scratch Reg
11 Reset Switch Enable (1 - reboot when dio9 low)
10 Enable CAN on DIO pins
9 Reserved
8 Latched mode1 value
7:4 Board Submodel (0x0 on production units)
3:0 FPGA revision
0x4 15:0 Random data changed every 1 second
0x6 15:4 Reserved
3 Lattice tagmem clock
2 Lattice tagmem serial in
1 Lattice tagmem CSn
0 Lattice tagmem serial out
0x8 15:0 DIO 15:0 input data
0xa 15:0 DIO 15:0 output data
0xc 15:0 DIO 15:0 data direction (1 - output)
0xe 15:0 DIO 31:16 input data
0x10 15:0 DIO 31:16 output data
0x12 15:0 DIO 31:16 data direction (1 - output)
0x14 15:0 DIO 47:32 input data
0x16 15:0 DIO 47:32 output data
0x18 15:0 DIO 47:32 data direction (1 - output)
0x1a 15:0 DIO 63:48 input data
0x1c 15:0 DIO 63:48 output data
0x1e 15:0 DIO 63:48 data direction (1 - output)
0x20 15:6 Reserved
5:0 DIO 69:64 input data
0x22 15:6 Reserved
5:0 DIO 69:64 output data
0x24 15:6 Reserved
5:0 DIO 69:64 data direction (1 - output)
0x26 16:0 EVGPIO Data register
0x28 16:0 EVGPIO Data Direction register
0x2a 15:0 Watchdog
0x2c 15:0 Reserved
0x2e 15:14 Reserved
13 SD#1 LED blink enable
12 SD#1 LED enable
11 SD#0 LED blink enable
10 SD#0 LED enable
9:8 Reserved
7:0 Override DIO and use as TXEN for respective XUART

Temperature Sensor

This SBC includes temperature sensors located on the CPU and RTC. Both of these can be read using tshwctl:

tshwctl --rtcinfo
tshwctl --cputemp

These commands will return the temperature of the RTC or internal CPU die temperature. Note that the --rtcinfo option will also return other information, See the Battery Backed RTC and Temperature Sensor section for more information.


The USB host port is a standard USB 2.0 at 480Mbps. The Linux kernel provides most of the USB support, and some devices may require a kernel recompile. For creating custom USB support, libusb may be the easiest route.

USB 5V Power can be disabled or re-enabled using DIO 69, marked EN_USB_5V on the schematic. See the syscon for more information on using this bit.

See the WIFI-N-USB manual for information on our WIFI support.


By default there is a /dev/watchdog with the tshwctl daemon running at the highest possible priority to feed the watchdog. This is a pipe that is created in userspace, so for many applications this may provide enough functionality for the watchdog by verifying that userspace is still executing applications. If you would like to have the watchdog functionality more tightly integrated with your application you can specify various feed options.

At the lower level there are 3 valid watchdog feed values that are written to the watchdog register in the #Syscon:

Value Result
0 feed watchdog for another .338s
1 feed watchdog for another 2.706s
2 feed watchdog for another 10.824s
3 disable watchdog

The watchdog is armed by default for 10s for the operating system to take over, after which the startup scripts autofeed the watchdog with:

echo a2 > /dev/watchdog

The /dev/watchdog fifo accepts 3 types of commands:

Value Function
f<3 digits> One time feed for a specified amount of time which uses the 3 digit number / 10. For example, "f456" would feed for 45.6 seconds.
"0", "1", "2", "3" One time feed with the value in the above table.
a<num 0-3> This value autofeeds with the value in the above table.

Most applications should use the f<3 digits> option to more tightly integrate this to their application. For example:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

void do_some_work(int data) {
	/* The contract for sleep(int n) is that it will sleep for at least n
	 * seconds, but not less.  If other kernel threads or processes require
	 * more time sleep can take longer, but when your process has a high
	 * priority this is usually measured in millseconds */

int read_some_io() {
	/* If this function (or do_some_work) misbehave and stall thee watchdog 
         * will not be fed in the main loop and cause a reboot.  You can test 
         * this by uncommenting the next line to force an infinite loop */
	// while (1) {}
	return 42;

int main(int argc, char **argv)
	int wdfd;
	/* In languages other than C/C++ this is still essentially the same, but
	 * make sure you are opening the watchdog file synchronously so the writes
	 * happen immediately.  Many languages will buffer writes together to make 
	 * them more efficient, but the watchdog needs the writes to be timed 
	 * precisely */
	wdfd = open("/dev/watchdog", O_SYNC|O_RDWR);

	while (1) {
		int data;
		/* This loop is expected to take about 5-6 seconds, but to allow some
		 * headroom for other applications, I will feed the watchdog for 10s. */
		write(wdfd, "f100", 4);

		data = read_some_io();


The XUART controller is a core we have included in the FPGA, as well as a userspace application called xuartctl for accessing these UARTs. Rather than using a kernel driver with the standard serial interface, we have implemented the XUARTs with features to simplify application development. The XUARTs allow you to easily use arbitrary baud rates, nonstandard modes such as DMX or 9n1, and they allow a very low latency operation. The XUART layer also uses the very low overhead TCP layer which allows you to transport serial over the network without writing any code.

The simplest example to get started is to define the port with:

xuartctl --server --port=1 --speed=115200

This will return "ttyname=/dev/pts/0", or a higher pts number. You can use this /dev/pts/# device to access the UART, but note that the pts device number can change based on other ssh, telnet or other processes. See this section for a sample script to setup the XUARTs with a predictable device name.

For more information and detailed usage, see the xuartctl page.

ttyS0 TTL 44_7 44_8 N/A N/A
XUART0 TTL 44_5 44_6 44_37 26_1
XUART1 TTL 44_19 44_20 44_27 26_2
XUART2 TTL 44_21 44_22 44_28 26_3
XUART3 TTL 44_23 44_24 44_38 26_4
XUART4 TTL 44_25 44_26 44_39 26_5
XUART5 TTL 44_31 44_32 44_29 26_6
XUART6 TTL 44_33 44_34 44_30 26_7
XUART7 TTL 44_35 44_36 44_40 26_8

See the 44 Pin Header and 26 Pin Header sections for more information on pin numbering.

External Interfaces

44 Pin Header

On our development baseboard the 4 JTAG pins are left hanging. The CN-PC104-40PIN-F is available as a mating connector to this header. TS-7500-TS-752-40pins.jpg

All pins labelled DIO are coming directly off of the FPGA and are rated for 3.3V, and can sink or source 12mA.

Pin Name
44_1 JTAG_DOUT [1]
44_2 JTAG_TMS [1]
44_3 JTAG_CLK [1]
44_4 JTAG_DIN [1]
44_5 DIO_05 / XUART0 TX / MODE2
44_6 DIO_06 / XUART0 RX
44_9 #External Reset / DIO_09
44_10 AUX_3.3V [2]
44_11 DIO_11 / SPI CS#
44_12 DIO_12 / SPI_MISO
44_13 DIO_13 / SPI_MOSI
44_14 DIO_14 / SPI_CLK
44_15 5V
44_16 GND
44_17 I2C_CLK
44_18 I2C_DAT
44_19 DIO_19 / XUART1 TX
44_20 DIO_20 / XUART1 RX
44_21 DIO_21 / XUART2 TX
44_22 DIO_22 / XUART2 RX
44_23 DIO_23 / XUART3 TX / CAN_TX
44_24 DIO_24 / XUART3 RX / CAN_RX
44_25 DIO_25 / XUART4 TX
44_26 DIO_26 / XUART4 RX
44_27 DIO_27 / XUART1 TXEN
44_28 DIO_28 / XUART2 TXEN
44_29 DIO_29 / XUART5 TXEN
44_30 DIO_30 / XUART6 TXEN
44_31 DIO_31 / XUART5 TX
44_32 DIO_32 / XUART5 RX
44_33 DIO_33 / XUART6 TX
44_34 DIO_34 / XUART6 RX
44_35 DIO_35 / XUART7 TX
44_36 DIO_36 / XUART7 RX / ADC_1
44_37 DIO_37 / ADC_2 / XUART0 TXEN
44_38 DIO_38 / ADC_3 / XUART3 TXEN
44_39 DIO_39 / ADC_4 / XUART4 TXEN
44_40 DIO_40 / XUART7 TXEN
44_41 POE_RX [3]
44_42 POE_78 [3]
44_43 POE_45 [3]
44_44 POE_TX [3]
Pin Layout
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
  1. 1.0 1.1 1.2 1.3 These pins are used for factory programming only. There is a software mechanism for soft reloading the FPGA documented here.
  2. This 3.3V rail can be used to power external peripherals with up to 500mA of current
  3. 3.0 3.1 3.2 3.3 The POE pins are the signal pairs from the ethernet connector which are required for a power over ethernet implementation. See the TS-752 baseboard schematic for an example implementation.

26 Pin Header

All pins labelled DIO are coming directly off of the FPGA and are rated for 3.3V, and can sink or source 12mA.

Pin Name
26_1 DIO 41 / XUART0 CTS# (input)
26_2 DIO 42 / XUART1 CTS# (input)
26_3 DIO 43 / XUART2 CTS# (input)
26_4 DIO 44 / XUART3 CTS (input)#
26_5 DIO 45 / XUART4 CTS# (input)
26_6 DIO 46 / XUART5 CTS# (input)
26_7 DIO 47 / XUART6 CTS# (input)
26_8 DIO 48 / XUART7 CTS# (input)
26_9 DIO 49
26_10 DIO 50
26_11 DIO 51
26_12 DIO 52
26_13 DIO 53
26_14 DIO 54
26_15 DIO 55
26_16 DIO 56
26_17 DIO 57
26_18 DIO 58
26_19 DIO 59
26_20 DIO 60
26_21 DIO 61
26_22 DIO 62
26_23 GND
26_24 DIO 64
26_25 DIO 65
26_26 DIO 66
Pin Layout
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


See the TS-752 page for more information on the other available connectors.

Revisions and Changes

FPGA Changelog

Revision Changelog
  • Initial release
  • Clock resynchronizer for the mode register
  • GSR is now layered through a resynchronizer for safer reset removal
  • Cleaned up SPI code
  • Set to use DIO 69:5, DIO 69 is USB power, 68 is ETH LED
  • Tristate mode pins after 181ms
  • Brought in EVGPIO fixes from TS-7700 project
  • Automatically enable driving of SPI when any CS is asserted, otherwise pins are DIO
  • Set up bit 10 of 0x2 in Syscon to enable CAN on DIO pins.

PCB Revisions

Revision Changelog
  • Initial release
  • Brought out CAN lines from CPU through FPGA

Software Images

Image File Changelog Known Issues
  • Initial release
  • 2GB Image
  • Added TS-8150 support.
  • Added TS-8920 support.
  • Implemented default splash screen, see /ts/splash
  • Audio support (sgtl5000, wm8750, sii9022)
    • Audio startup noise added, see /ts/startup.wav
  • AutoStart X11 in the initramfs
    • Configure started apps with /ts/initramfs-xinit
  • Default x session changed to icewm-lite for faster boot time
  • ifplugd is no longer run when jp1 is set due to race condition
    • Configure the network in Debian once you are booting there
  • check-usb-update implemented
    • Plug in a USB drive with 1 partition containing /tsinit which will automatically run. Used primarily for production.
  • Marvell Switch Chip fixes added
  • Wheezy updated to latest in repository
  • Added support for both onboard/offboard switches
    • Used in cases such as TS-4600 + TS-8700
  • Xuartctl defaults to 100hz instead of IRQ driven
    • IRQ behavior is specifically tuned for best latency, but requires high CPU
  • ts-sendsigs-omit script fixes so multiple nbd-clients or xuartctls are not killed early in shutdown
  • Root filesystem is now always /dev/rootfs
  • TS-8700 switch reset race condition fixed
  • tshwctl minor fixes
    • ethinfo overflow fixed
    • tagmem is now only written if value actually changed with setjp/removejp/setmac
  • Image sized for already shipping 4GB MicroSD cards
  • Unionfs disabled due to kernel panics
  • Added support for DoubleStore formatted SD cards
  • TS-8400 support completed
  • ifpulgd now starts on switch interfaces correctly
  • /ts/config file create to allow for further configuration of the initramfs. See this file for more information.
  • Soft Jumpers 2,3,4,5 have been removed and are implemented in the /ts/config file which allows more than 8 settings
    • The config file allows enabling and configuring utilities like ifplugd, xuartctl, mdnsd, and more.
    • Read only jumper 4 removed due to bugs with unionfs.
    • Behavior of JP1 and JP8 are not changed
    • JP7 added to minimize initramfs initialization for fastest boot.
  • X11 in Debian started with correct HOME variable so a valid .Xauthority file is created
    • This allows DISPLAY=:0 to work, and fixes some dns resolution issues
  • /etc/init.d/motd updated to include additional information for debugging
  • Initramfs will correctly wait for SD cards to detect, prior image created race condition potential
  • On all boots, always load base USB drivers. OTG needs special load order to work properly as host (top USB port is OTG)
  • resolv.conf in initramfs and Debian will correctly use DHCP assigned servers
  • Removed udev persistent-net-rules that was present in previous image
  • Removed /ts/fastboot that was present in previous image
  • Moved getrtc so it is always run
  • Fixed race condition by waiting until disk is detected
  • Removed unnecessary modprobes
  • Made sure USB modules get fully loaded
  • Cleaned up RTC code and implemented better locking around I2C functions
  • Removed unused bootdev
  • Issue with tshwctl and nbus that can cause corrupt transactions if tshwctl nbus commands run repeatedly in a quick loop
  • Added back in external_temp measurements on 4600 and 7600
  • Change --ethwlan short option to -5, no longer conflicts
  • Changed mux to make sure USB ID pin stays in place
  • Implement Marvell's workaround for errata 3.1 for 10mbps connections
  • Added help output for resetswitch* for all mx28s
  • Fixed numbering error in rtcinfo
  • Add 2s to current time to account for delay in writing to RTC
  • Issue with tshwctl and nbus that can cause corrupt transactions if tshwctl nbus commands run repeatedly in a quick loop
  • Fixed issue with tshwctl nbus setup process that could cause corrupt transactions
  • Added CAN modules and pin setup
  • Updated X input driver to allow for custom calibration data at /slib/ts_calib
  • Created necessary calibration file for TS-4600/8380
  • Automatically enable audio output for TS-4600/8380
  • A separate command is needed to enable DIO override for CAN on the TS-7600. In the next release this will be added as a flag to `tshwctl` for easier activation.
  • Added CAN enable option to tshwctl
  • Fixed tshwctl bug in get/set/clr dio operations
  • Added ability to swap and invert touch axes in /slib/ts_calib
  • Increase max timeout for spiflashctl --erase command to what is specified in datasheet
  • Fixed issues with both NBD and DoubleStore; greatly increased reliability over operational lifetime
  • Brought in patches to FEC PHY driver. Previous kernels have chance for ping-pong link establishment or failed link establishment with certain network partners
  • Fixed MUXBUS bug on TS-4600. NBUS transactions that can delay too long can cause the transaction to hang. Only observed with MUXBUS access.
  • Resolved rare event where switch IC would receive a double reset.
  • Added support for new SPI flash chips

Product Change Notices

SPI Flash Vendor Change

Due to an EOL notice, the SPI flash on this product is changing. The old part is a Micron N25Q064A13ESE40F. Two new parts were qualified to reduce the impact of any potential EOL in the future. The new parts are the Microchip's SST26VF064BA, and ISSI's IS25LP064A.

Most applications will not be affected by this change unless a custom kernel and initramfs are being written to the SPI flash. In those cases some updates will be required.

Linux Kernel Changes

Rebuilding the latest kernel in our git will include support for these these changes, but the specific commit where the fix is applied is available here: linux-

This change specifically adds support for the SPI devices to the command 'spiflashctl' which is a binary included in the initramfs. The change to 'spiflashctl' is the only change that is required for proper support.

Images with support

Any of our Linux images after March 8th, 2018 include support for this new SPI flash.

Product Notes

FCC Advisory

This equipment generates, uses, and can radiate radio frequency energy and if not installed and used properly (that is, in strict accordance with the manufacturer's instructions), may cause interference to radio and television reception. It has been type tested and found to comply with the limits for a Class A digital device in accordance with the specifications in Part 15 of FCC Rules, which are designed to provide reasonable protection against such interference when operated in a commercial environment. Operation of this equipment in a residential area is likely to cause interference, in which case the owner will be required to correct the interference at his own expense.

If this equipment does cause interference, which can be determined by turning the unit on and off, the user is encouraged to try the following measures to correct the interference:

Reorient the receiving antenna. Relocate the unit with respect to the receiver. Plug the unit into a different outlet so that the unit and receiver are on different branch circuits. Ensure that mounting screws and connector attachment screws are tightly secured. Ensure that good quality, shielded, and grounded cables are used for all data communications. If necessary, the user should consult the dealer or an experienced radio/television technician for additional suggestions. The following booklets prepared by the Federal Communications Commission (FCC) may also prove helpful:

How to Identify and Resolve Radio-TV Interference Problems (Stock No. 004-000-000345-4) Interface Handbook (Stock No. 004-000-004505-7) These booklets may be purchased from the Superintendent of Documents, U.S. Government Printing Office, Washington, DC 20402.

Limited Warranty

See our Terms and Conditions for more details.

WARNING: Writing ANY of the CPU's One-Time Programmable registers will immediately void ALL of our return policies and replacement warranties. This includes but is not limited to: the 45-day full money back evaluation period; any returns outside of the 45-day evaluation period; warranty returns within the 1 year warranty period that would require SBC replacement. Our 1 year limited warranty still applies, however it is at our discretion to decide if the SBC can be repaired, no warranty replacements will be provided if the OTP registers have been written.


Arm9 is a trademark, and Arm is a registered trademark, of Arm Limited (or its subsidiaries) in the US and/or elsewhere.