From embeddedTS Manuals
Product Page
Product Images
Mechanical Drawing
Mechanical Drawing
FTP Path
Freescale i.MX515 Documentation
i.MX515 Product Page
Reference Manual


The TS-8100 baseboard is an upgrade path from our TS-7350/TS-7370 series providing a PC104 bus, multiple serial ports, LCD, and a DIO header.

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.

TS-8100 Options and Accessories

TS-8100 Options
Item Description
TS-8100 The TS-8100 is a TS-Socket Baseboard including a PC104 bus, DIO, RS232, RS485, and an optional second ethernet.
OP-8100-ETH The optional "OP-ETH" replaces the push button and instead populates a second ethernet port. This utilizes a USB ethernet which will work with any System-on-Module.
OP-CAN2-485 The optional "OP-CAN2-485" includes the optional second #CAN and RS485 transceiver.
OP-STHRU-64 The option "OP-STHRU-64" includes a 64 pin PC104 connector that extends the pins so the TS-8100 does not need to be the base of the PC104 stack. This option is not required for stacking PC104 peripehral on the TS-8100 as the peripherals include a stack through connector.

Other options include:

Item Description
RC-DB9 The RC-DB9 brings out the 10 pin connection to a DB9. See the RC-DB9 page for the exact pinout. The red line indicates pin 1 and should be lined up with the white dot on the board.
CB-USB-AF5P The CB-USB-AF5P connects from a standard 5 pin 0.1" pitch header to a USB A host. This can be used to expose a single USB port while keeping the rest internal to your own enclosure.
CB7-05 The CB7-05 is a 5 foot null modem cable. This is commonly used to connect to your workstation.
KPAD The KPAD is a 4x4 keypad compatible with the DIO header on the TS-81XX.
KPAD-LCD 2x24 Alphanumeric Display with 4x4 keypad mounted on a plate.

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-8100-4800 accepts 5-28VDC input connected to the two terminal blocks.

Power connector

A typical power supply for just the TS-8100-4800 will allow around 1A, but a larger power supply may be needed depending on your peripherals.

Once you have applied power to your baseboard you should look for console output. Creating this connection is described more in the next chapter, but the first output is from the bootrom:

  >> TS-BOOTROM - built Dec 21 2011 10:05:44
  >> Copyright (c) 2011, Technologic Systems
  >> Booting from microSD card ...

The 3 dots after indicate steps of the booting procedure. The first dot means the MBR was copied into memory and executed. The next two dots indicate that the MBR executed and the kernel and initrd were found and copied to memory.

The "Booting from XXXX...." message will indicate your boot media. On the TS-8100 this is controlled by the "SD Boot" jumper hear the ethernet connectors:


Get a Console

The TS-8100 console is an RS232 UART at 115200 baud, 8n1 (8 data bits 1 stop bit), and no flow control. On the TS-8100 you need to set the "Console Enable" jumper as pictured:


This will bring the console UART to both the DB9 Port, and the COM1 Header.

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

Initrd / Busybox

When the board first boots you should see output similar to this:

 >> TS-BOOTROM - built Jan 24 2011 12:40:54
 >> Copyright (c) 2010, Technologic Systems
 >> Booting from SD card...
 Finished booting in 1.11 seconds
 Type 'tshelp' for help

This is a busybox shell which presents you with a very minimalistic system. This filesystem is loaded into memory, so none of the changes will be saved unless you type the command


or mount a filesystem as read/write. This can also provide a simple mechanism for running your application in an entirely read-only environment. The linuxrc script will be the first thing executed as soon as the kernel is loaded. This sets the default IP address, loads a reloadable FPGA bitstream if one is present, starts the userspace ctl applications, and more. Read the linuxrc for more information.

While busybox itself doesn't contain much functionality, it does mount the Debian partition under /mnt/root/. It will also add common paths and load libraries from the Debian system. Many of the Debian applications will work by default. For example, if you are using the TS-4800 with a video interface (or a touchpanel like the TS-TPC-8390), you will see icewm startup. The linuxrc will determine if the baseboard is one that is recognized with video, and start X11 with icewm from Debian. This is why it has the Debian logo since it uses their theme files, but is not usable as Debian. This is also only provided as a demo of X11 and not intended to be used for development. Whether or not a Debian application will work in fastboot needs to be judged per application. If an application relies on certain paths being in certain places, or running services, you should instead boot to Debian to run them.

If you want to install applications using apt or building them from source you should to Debian for a more complete environment. Most build processes are not designed to work with busybox providing the shell and utilities.

This shell when started on the COM port is what is blocking a Debian boot. If you close it by typing


the boot process will continue. If you are connected through telnet, this will instead open up its own instance of the shell so typing


will only end that session. Through any connection method you can relink the linuxrc to change it to boot by default to Debian.

The initrd has these boot scripts available:

Script Function
linuxrc-fastboot (default) Boots immediately to a shell in ramdisk. This will mount whichever boot medium you have selected to /mnt/root/. When you type 'exit', it will boot to that medium.
linuxrc-sdroot Boots immediately to the Debian stored on SD.
linuxrc-xnandroot Boots immediately to the Debian stored on the XNAND.
Note: Keep in mind the boot medium is selected by the pinout on your baseboard, not through software. The ramdisk and kernel will be loaded before the linuxrc begins execution.

For example, to set the linuxrc to boot immediately to Debian on SD or NAND, you would run this:

rm linuxrc; ln -s /linuxrc-sdroot /linuxrc; save

You can edit the linuxrc-4800 to create more booting methods (NFS, USB, etc).

The small default initrd is only 2Mbyte but there is space for approximately 300 Kbyte of additional user applications. The binaries on the initrd are dynamically linked against embedded Linux's "uclibc" library instead of the more common Linux C library "glibc". "uclibc" is a smaller version of the standard C library optimized for embedded systems and requires a different set of GCC compiler tools which are available here.

 The compiled instance of busybox includes several internal commands listed below:
 BusyBox v1.14.2 (2011-02-03 15:49:11 MST) multi-call binary
 Copyright (C) 1998-2008 Erik Andersen, Rob Landley, Denys Vlasenko
 and others. Licensed under GPLv2.
 See source distribution for full notice.
 Usage: busybox [function] [arguments]...
    or: function [arguments]...
         BusyBox is a multi-call binary that combines many common Unix
         utilities into a single executable.  Most people will create a
         link to busybox for each function they wish to use and BusyBox
         will act like whatever it was invoked as!
 Currently defined functions:
         [, [[, ar, ash, basename, cat, chat, chgrp, chmod, chown, chroot,
         cmp, cp, cpio, cttyhack, cut, date, dd, depmod, devmem, df, dirname,
         dmesg, du, echo, egrep, env, expr, false, fdisk, fgrep, find, grep,
         gunzip, gzip, halt, head, hostname, hush, ifconfig, insmod, kill,
         killall, ln, login, ls, lsmod, md5sum, mdev, mkdir, mknod, modprobe,
         more, mount, msh, mv, netstat, ping, pivot_root, poweroff, printf,
         ps, pwd, reboot, rm, rmdir, rmmod, route, rx, sed, setconsole,
         setsid, sh, sleep, stty, sync, tail, tar, telnetd, test, tftp,
         top, tr, true, udhcpc, umount, uname, unzip, usleep, uudecode,
         uuencode, vi, wget, xargs, yes, zcat

Also on the initrd are the TS specific applications: nandctl, ts4800ctl, and xuartctl. We also provide the ts.subr which provides the following functions:


By default, linuxrc will not insert the necessary modules into the kernel to mount and use USB devices within the initrd/busybox environment if there is no USB device present upon bootup (USB support is enabled by default within the Debian environment). The quickest way to get a USB device (like a USB thumb drive) to mount in the initrd/busybox environment is to ensure that it is plugged in before the SBC is powered up. In order to get hot-swappable USB devices regardless of device presence at bootup time, you must "modprobe" the necessary modules. This has been done for you in the ts.subr file with the usbload() function.

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 initrd 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.

To make your changes permanent in the initrd you will need to edit the linuxrc script. Use the same commands you would use to manually configure it and place them over the current ifconfig calls.

Installing New Software

Debian provides the apt-get system which lets you manage pre-built applications. Before you do this you need to update Debian's list of package versions and locations. This assumes you have a valid network connection to the internet.

Note: The NAND image is based on the emdebian project which is no longer maintained.

Debian Squeeze has been moved to archive so you will need to update /etc/apt/sources.list to contain only these two lines:

 deb http://archive.debian.org/debian squeeze main
 deb-src http://archive.debian.org/debian squeeze main
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
     /sbin/ts4700ctl --redledon
     ## If you are launching a daemon or other long running processes
     ## this should be started with
     # nohup /usr/local/bin/yourdaemon &
     /sbin/ts4700ctl --redledoff
     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

To make your application startup from the initrd you only need to add this from the linuxrc script. Usually the best place to add in your application is right after /mnt/root/ is mounted so the Debian libraries and applications are available.

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

For imaging the SD card we recommend using a Linux or similar operating system that allows you to access a block device using dd. We do not support rewriting the SD card from Windows.

If you are reprogramming the SD card from your workstation you will also need to determine the SD card device. Once you have connected the SD card to your workstation you can usually find the correct block device in the output of "dmesg". For example:

 [  309.498834] sd 8:0:0:0: [sdb] 3862528 512-byte logical blocks: (1.97 GB/1.84 GiB)
 [  309.519814] sd 8:0:0:0: [sdb] Write Protect is off
 [  309.519818] sd 8:0:0:0: [sdb] Mode Sense: 03 00 00 00
 [  309.519819] sd 8:0:0:0: [sdb] Assuming drive cache: write through
 [  309.536025] sd 8:0:0:0: [sdb] Assuming drive cache: write through
 [  309.536029]  sdb: sdb1 sdb2 sdb3
 [  309.559672] sd 8:0:0:0: [sdb] Assuming drive cache: write through
 [  309.559676] sd 8:0:0:0: [sdb] Attached SCSI removable disk

On this system my SD card block device is /dev/sdb, but your system will likely be different. The block devices are allocated in order by the letter so the next USB drive connected would be /dev/sdc. On some newer kernels you will see '/dev/mmcblk0' as the block device and '/dev/mmcblkop1' for the first partition. For these examples I will use the '/dev/mmcblk0' format.

WARNING: Many distributions will name your hard drive something like /dev/sda or /dev/hda which will have the same naming scheme as an SD card or a USB drive. Make sure you are aware which device is which before writing the disk.

The TS-4800 Debian distribution is available as a 2 GB disk image.

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

Once downloaded you can decompress the image using gunzip:

gunzip 2gbsd-latest.dd.gz

The resulting file will be "2gbsd-latest.dd".

If you are working with the SD card from your workstation, keep in mind that most Linux distributions will mount the partitions that they can as soon as the drive is inserted. This is desirable if you want to open the filesystem, but for dealing directly with the block device for performing backups or restoring an image this is dangerous to your data.

To verify if your workstation has mounted the block device on insertion:

# look for your SD card block device to see if this is already mounted.
# use umount on each path listed as mounted to 
umount /media/sd-part1

Restore from Workstation

To write the latest image or restore to stock you would use the dd command. This will perform a byte for byte copy from our image. This contains the MBR boot code with the partition tables, the kernel, initrd, and Debian filesystem. No other formatting or partitioning is needed.

Write an image to the entire SD card:

dd if=/path/to/backup.dd of=/dev/mmcblk0 bs=32k conv=fsync

If you want to write a new kernel, but not an entire image you can rewrite the second partition:

dd if=/path/to/zImage bs=32k of=/dev/mmcblk0p2 conv=fsync

Backup from Workstation

To backup an entire SD card image:

dd if=/dev/mmcblk0 of=/path/to/backup.dd bs=32k

This will create a dd file the size of the card.

Note: A MicroSD card from one manufacturer will likely not be the exact same size as another manufacturer's MicroSD card of the same size. Our partition layouts by default leave the last 10% of the images unallocated to account for the size difference of various manufacturers MicroSD cards. As long as you use our partition layout you should not need to be concerned with this, but if you create your own layout we strongly recommend leaving 10% of the disk unallocated.

If you would like to backup just the Kernel partition, you would grab partition 2.

dd if=/dev/mmcblk0p2 of=/path/to/zImage bs=32k

Restore From the TS-4800

To write the latest image or restore to stock you would use the dd command. This will perform a byte for byte copy from our image. This contains the MBR boot code with the partition tables, the kernel, initrd, and Debian filesystem. No other formatting or partitioning is needed.

Write an image to the entire SD card:

dd if=/path/to/2GB-mSD-4800-latest.dd of=/dev/mmcblk0 conv=fsync


dd if=/mnt/root/zImage of=/dev/mmcblk0p2 conv=fsync

Backup From the TS-4800

To backup an entire SD card image:

dd if=/dev/mmcblk0 of=/path/to/backup.dd bs=32k

This will create a dd file the size of the card.

Note: A MicroSD card from one manufacturer will likely not be the exact same size as another manufacturer's MicroSD card of the same size. Our partition layouts by default leave the last 10% of the images unallocated to account for the size difference of various manufacturers MicroSD cards. As long as you use our partition layout you should not need to be concerned with this, but if you create your own layout we strongly recommend leaving 10% of the disk unallocated.

If you would like to backup just the kernel partition, you would grab partition 2.

dd if=/dev/mmcblk0p2 of=/path/to/zImage bs=32k


The XNAND is restorable from the stock SD card. Boot the the initrd (not Debian) and you can run one command to restore the stock mini-lenny distribution:

# createxnandroot
Preparing XNAND flash drive...
Copying Kernel and ramdisk from SD to XNAND...
Creating root filesystem...

This is normally the only necessary procedure, however under catastrophic circumstances (such as accidental dd onto the XNAND device), it may be necessary to run createxnandboot first.

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.

You can use the TS-4800 single precision FPU with these gcc arguments:

 -march=armv7-a -mtune=cortex-a8 -mfpu=neon -ftree-vectorize -mfloat-abi=softfp

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.

Cross Compiling

While you can develop entirely on the board itself, if you prefer to develop from another x86 compatible Linux system we have a cross compiler available. For this board you will want to use this toolchain. To compile your application, you only need to use the version of GCC in the cross toolchain instead of the version supplied with your distribution. The resulting binary will be for ARM.

[user@localhost]$ /opt/4800/arm-2008q3/bin/arm-none-linux-gnueabi-gcc hello.c -o hello
[user@localhost]$ file hello
hello: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.14, not stripped

As with compiling onboard, the recommended compiling options are:

-march=armv7-a -mtune=cortex-a8 -mfpu=neon -ftree-vectorize -mfloat-abi=softfp

This is one of the simplest examples. If you want to work with a project, you will typically create a makefile. You can read more about makefiles [here]. Another common requirement is linking to third party libraries provided by Debian on the board. There is no exact set of steps you can take for every project, but the process will be very much the same. Find the headers, and the libraries. Sometimes you have to also copy over their binaries. In this example, I will link to sqlite from Debian.

Install the sqlite library and header on the board:

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

This will fetch the binaries from the internet and install them. You can list the installed files with dpkg:

dpkg -L libsqlite3-0 libsqlite3-dev

The interesting files from this output will be the .so files, and the .h files. In this case you will need to copy these files to your project directory.

I have a sample 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 I have the makefile below. This will have to be adjusted for your toolchain path. In this example I placed the headers 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

You can then copy this directly to the board and execute it. 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 you are frequently updating data, but will require more setup. See your linux distribution's manual for more details. The simplest network method is using ssh/sftp. You can use winscp if from windows, or scp from linux. Make sure you set a password from debian for root. Otherwise the ssh server will deny connections. From winscp, enter the ip address of the SBC, the root username, and the password you have set. This will provide you with an explorer window you can drag files into.

For scp in linux, run:

#replace with your app name and your 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

WARNING: Backup any important data on the board before continuing.

For adding new support to the kernel, or recompiling with more specific options you will need to have an X86 compatible linux host available that can handle the cross compiling. Compiling the kernel on the board is not supported or recommended. Before building the kernel you will need to install a few support libraries on your workstation:



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


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

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

Set up the Sources and Toolchain

# Download the cross compile toolchain (EABI) from Technologic Systems:
wget ftp://ftp.embeddedTS.com/ts-socket-macrocontrollers/ts-4800-linux/cross-toolchains/arm-2008q3-2.tar.gz

# Create target directory and extract:
sudo mkdir /opt
sudo mkdir /opt/4800
sudo tar -xvzf arm-2008q3-2.tar.gz -C /opt/4800/

# Download the kernel sources
wget ftp://ftp.embeddedTS.com/ts-socket-macrocontrollers/ts-4800-linux/sources/linux-2.6.35-ts-latest.tar.gz

#Extract the Kernel Sources
tar -xvf linux-2.6.35-ts-latest.tar.gz

cd linux-2.6.35-ts/

The kernel sources need a few variables to be exported.

# Set the CROSS_COMPILE variable to the absolute path to the toolchain.  This will be different for your system:
export CROSS_COMPILE=/opt/4800/arm-2008q3/bin/arm-none-linux-gnueabi-

# Normally, ARCH will be set based on your build hosts architecture.
export ARCH=arm

This sets up the default configuration that we ship with for the TS-4800

make ts4800_defconfig
make menuconfig

This will bring up a graphical menu where you can edit the configuration to include support for new devices. For Example, to include support for a Prolific USB to serial adapter you would go to 'Device Drivers -> USB Support-> USB Serial Support' and then select 'USB Prolific 2303 Single Port Serial Driver'. Since the kernel only has a limited space, build drivers as modules whenever possible.

make menuconfig

Build the kernel Once you have it configured, start building. This usually takes a few minutes.

make && make modules

The new kernel will be at "arch/arm/boot" in a compressed format called zImage. The uncompressed version is simply called Image. It is required that the kernel fit in your partition #2 on the SD card (or 1 on the XNAND). If you need to shorten the size, try including your changes to the kernel as modules instead. Otherwise you will need to resize the kernel partition to account for the size difference.

Install the kernel Now that you have a kernel you can install it as you would our stock. See the #Backup / Restore section for examples on writing this to disk.

To install the modules:

mkdir newmodules
INSTALL_MOD_PATH=newmodules make modules_install

# Replace /dev/sdb with your sd card
mkdir /mnt/miniSD{3,4}
mount /dev/sdb4 /mnt/miniSD4/
mount /dev/sdb3 /mnt/miniSD3/

# Remove old modules in initrd
rm /mnt/miniSD3/mx_sdhci.ko
rm /mnt/miniSD3/mxc_ipuv3_fb.ko

# Update initrd drivers
cp ./drivers/mmc/host/mx_sdhci.ko /mnt/miniSD3/
cp ./drivers/video/mxc/mxc_ipuv3_fb.ko /mnt/miniSD3/

# Remove existing modules on SD
rm -r /mnt/miniSD4/lib/modules/*
cp -r newmodules/* /mnt/miniSD4/

umount /mnt/miniSD3
umount /mnt/miniSD4
sync && sync

After you install the new modules, you will need to boot the kernel and run "depmod -a" to rebuild the dependency map. You can them use modprobe to load the individual modules. You can also copy individual modules to your existing kernel assuming the kernel is the exact same version as the installed one and you use the same toolchain.


The tsctl library and network service is available to simplify communicating with many peripherals and standard TS devices such as DIO, CAN, SPI, I2C, PC104, and more. The API supports a C API, TCP binary interface, text protocol, and presents a JSON service to allow for flexible options to interface with hardware.

You can download the tsctl sources here. To build tsctl you should connect the TS-4800 to the network. Once connected, you can follow the next steps to build and install tsctl.

First you must install libreadline which is used for the tsctl shell:

apt-get update
apt-get install libreadline5-dev -y

Once the dependencies are installed you can build tsctl. You can also use these next steps to update tsctl.

wget https://files.embeddedTS.com/apps/tsctl/libtsctl-src-LATEST.tar.gz
tar -xf libtsctl-src-LATEST.tar.gz
cd libtsctl

# This next command will take approximately 2.5 minutes
make tsctl

cp noncavium/tsctl /usr/local/bin/
chmod a+x /usr/local/bin/tsctl

Once installed tsctl can be launched from the command line using "tsctl". If your application relies on the server, you can launch:

tsctl --server

This will cause tsctl to fork into the background and look for incoming tcp connections.


ADC Core

The FPGA includes a core for communicating with the MCP3428 ADC controller we use on several of our baseboards. If you are using this on your own baseboard this core assumes the standard circuit which allows 2 differential channels and 4 single-ended channels. The single-ended channels are chosen using analog muxes controlled by the AN_SEL line. Since different baseboards use a different pin for AN_SEL, a register is also provided to select the correct lines.

Channels 1 and 2 are differential channels with a range of -2.048V to +2.048V. Channels 3-6 are 0 to 10.24V.

The channel mask register controls which channels are enabled. Bits 0-5 enable channels 1-6 respectively. If a given channel is not enabled, (enable bit == 0) it will not be sampled and its conversion value register will contain an obsolete and meaningless value. The more channels that are enabled, the lower the sampling speed on each channel.

Note: For all bit resolutions the ADC will output 1's compliment signed data. On the single-ended channels this means the best practice in calculating most voltages is to presume one bit less resolution. Eg. 11, 13, and 15 bits maximum value.
Offset Bits Description
0x0 15:8 Core ID register (reads 0xad)
7:6 Reserved
Analog Select Pin [1]
Value Description
0 Do not use an AN+SEL
1 use CN1 pin 77 for AN_SEL (TS-8100)
2 use CN1 pin 74 for AN_SEL (TS-8390)
3 Reserved
Value Description
0 240Hz, 12 bit resolution
1 60Hz, 14 bit resolution
2 15Hz, 16 bit resolution
3 Reserved
Programmable Gain Amplifier
Value Description
0 No gain
1 2x gain
2 4x gain
3 8x gain
0x2 15:0 Sample enable [2]
0x4 15:0 Channel 1 most recent conversion value
0x6 15:0 Channel 2 most recent conversion value
0x8 15:0 Channel 3 most recent conversion value
0xa 15:0 Channel 4 most recent conversion value
0xc 15:0 Channel 5 most recent conversion value
0xe 15:0 Channel 6 most recent conversion value
  1. This is used to select the mux to select between channels 2/3, 4/5.
  2. 1 = enable sampling, 0 = disable sampling (default)

Baseboard ID

The TS-8100 has a baseboard ID of 7 which can be used to detect the baseboard in code. Implementations should refer to "ts4800ctl --baseboard" which detect the baseboard ID.


The TS-4800's FPGA contains two SJA1000-compatible CAN controllers. The first CAN controller brings CAN RX and CAN TX signals to the baseboard through CN2 pins 97(TX) and 99(RX). The second CAN controller brings CAN2 RX and TX to CN1, pins 69(RX) and 71(TX). The TSCTL software library is used to access the functionality of the two CAN controllers. See tsctl for the software support of the CAN interface.

On the TS-8100 CAN is avaialble on:

Signal Location
CAN1_H DB9 pin 4 and COM1 header pin 4
CAN1_L DB9 pin 9 and COM1 header pin 9
CAN2_H [1] COM3 header pin 4
CAN2_L [1] COM3 header pin 9
  1. 1.0 1.1 If you have OP-CAN2-485, you also have a second CAN tranceiver.

See the tsctl page for documentation on communication with the CAN bus.


The TS-4800 features a Freescale i.MX515 800MHz ARM Cortex-A8 processor. For more information on the included features see the i.MX515 product page or the CPU Manual.


The TS-8100 has up to 28 DIO available:

Signal Location
8100_DIO_1 DIO header pin 1
8100_DIO_3 DIO header pin 3
8100_DIO_5 DIO header pin 5
8100_DIO_7 DIO header pin 7
8100_DIO_9 DIO header pin 9
8100_DIO_11 DIO header pin 11
8100_DIO_13 DIO header pin 13
8100_DIO_15 DIO header pin 15
8100_DIO_15 DIO header pin 15
8100_DIO_A16 PC104 header pin A16
8100_DIO_A17 PC104 header pin A17
8100_DIO_A18 PC104 header pin A18
8100_DIO_A19 PC104 header pin A19
8100_DIO_A20 PC104 header pin A20
8100_DIO_A21 PC104 header pin A21
8100_DIO_B11 PC104 header pin B11
8100_DIO_B12 PC104 header pin B12
8100_LCD_3 LCD header pin 3
8100_LCD_5 LCD header pin 5
8100_LCD_6 LCD header pin 6
8100_LCD_D1 LCD header pin 7
8100_LCD_D0 LCD header pin 8
8100_LCD_D3 LCD header pin 9
8100_LCD_D2 LCD header pin 10
8100_LCD_D5 LCD header pin 11
8100_LCD_D4 LCD header pin 12
8100_LCD_D7 LCD header pin 13
8100_LCD_D6 LCD header pin 14

These can be accessed using tsctl, or by interacting with the #TS-8100 Register Map.

Ethernet Port

The Freescale processor implements a 10/100 ethernet controller with support built into the Linux kernel. You can use standard Linux utilities such as ifconfig/ip to control this interface. See the #Configuring the Network section for more details. For the specifics of this interface see the CPU manual.


Any external interfaces called for by the TS-SOCKET specification that are not provided by the CPU are implemented in the FPGA whenever possible. The FPGA is connected to the CPU by a static memory controller, and as a result the FPGA can provide registers in the CPU memory space.

While most common functionality for the TS-4800 is accessed through layers of software that are already written, some features may require talking directly to the FPGA. To access the FPGA registers you need to use 16bit reads and writes to the base address 0xb0000000. All of our FPGA cores will be offsets from this address. While some cores have 8 bit registers, these are accessed using a memory window.

Register Map:

Offset Usage
0x00000 16KB blockram access (for XUART buffer)
0x10000 Syscon registers
0x11000 4KB MUXBUS space
0x12000 Touchscreen registers
0x13000 SPI Interface
0x14000 Motor Controller
0x15000 IRQ Controller
0x16000 ADC control register
0x18000 XUART Control registers
0x1a000 CAN0 8-bit Window
0x1b000 CAN1 8-bit Window
0x1c000 XUART 32-bit Window

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 /ts4800_bitstream.vme.gz on the initrd 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 Revision MUXBUS Touchscreen Motor SPI XUARTs CAN0 CAN1
Default 7 On On Off On On On On

We have had several changes to the FPGA during the lifetime for fixing any bugs that are found. You can use an opencore and reload the bitstream to get the latest fixes, though bootrom updates can only be applied by submitting an RMA for your board.

FPGA Revision Log
Revision Changes
  • Fixed reading of MAC address on boot
* Removed "IRQ Enable" bits from syscon.  Linux handles this automatically.
* Removed the unused mode3 bit
* CAN enable bit automatically set when accessing CAN registers
* XUART enable bit automatically set when accessing XUART registers
* Removed debug registers and other unused bits
* Cleaned up mode latching code
* Forced wb_spi to always be 8bit for data reg access, removes need of 16 to 8 bridge
* Dblstor routines added to bootrom
* Boot behavior to fallback to NAND if SD boot is on and SD fails
* Fixed early LED behavior in bootrom
* CAN resync fixups
* PC/104 muxed IRQs working
* MUXBUS bridge fixups, arbiter, cleanup
* Added CAN and PC/104 IRQ enables
* Added counters
* Added RS-422 enables
* Added ADC core
* Added memwindow for 8bit MUXBUS access
* WishBone resync fixups
5 Decrease counter code size
4 Bootrom fix for framebuffer reboot issue.

FPGA Programming

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

The opencore FPGA sources are available here.

We have prepared the opencore projects which gives you the ability to reprogram the FPGA while either preserving or removing our functionality as you choose. The code sources are in verilog, and we use Lattice Diamond to generate the JEDEC file. You can download Lattice Diamond from their site. You can request a free license, and it will run in either Windows or Linux (only Redhat is supported).

For more advanced changes you may look to opencores.org which has many examples of FPGA cores. To build the FPGA with your new changes, go to the 'Processes' tab and double-click 'JEDEC File'. This will build a jedec file in the project directory. On a linux system, either x86 compatible or ARM, we provide an application called jed2vme. You can find this in the initrd already on the TS-4800.

jed2vme for x86

We also have the sources here.

WARNING: Do not use the 'jed2vme' provided by Lattice. Their version writes to flash and as the opencores do not contain the bootrom this will brick your board.

jed2vme can be used like this:

jed2vme bitstream.jed | gzip > bitstream.vme.gz

To execute this on your board run this:

ts4800ctl --loadfpga=bitstream.vme
# or
ts4800ctl --loadfpga=bitstream.vme.gz

As space is constrained in the initrd it is suggested to gzip the file as shown in the jed2vme example. To load this bitstream automatically you can place it in the root of the initrd and name it '/ts4800_bitstream.vme.gz'. The linuxrc script will by default load this bitstream immediately on startup (before the fastboot shell).

The FPGA contains flash memory which contains Technologic System's default FPGA SRAM load. The "ts4800ctl --loadfpga" will not overwrite the flash memory of the FPGA and will only load the SRAM contents of the FPGA, making for an unbrickable system if something should go wrong. If something does go wrong, you can restore the onboard flash via the offboard flash or microSD card.


The TS-4800 has several IRQs that can be used by external devices. See chapter 3 of the CPU manual for a complete listing of all of the available IRQs. 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.

This example below will work with any of our TS-Socket boards running Linux. This 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;

With the TS-8100 all of these pins are available on the PC104 header:

IRQ # Name
275 IRQ5/DIO_00
276 IRQ6/DIO_01
277 IRQ7/DIO_02


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

Green Behavior Red behavior Meaning
On Off System is booted and running
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 Turns on 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 ts4800ctl 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 5V is being provided and the supply is capable of providing at least 1 A to the System-on-Module (SoM), an RMA is suggested.
On On The board is receiving too little power, or something is drawing too much current from the SoM's power rails.


The MUXBUS is the bus between the FPGA on the SoM to communicate with the off-board CPLD. The CPLD controls PC/104 access as well as some DIO. The MUXBUS config register in the Syscon allows enabling and configuring the speed for this bus. The MUXBUS timing also influences the communication with PC/104 peripherals.

For more advanced details on the MUXBUS, refer to the implementation details here.

Most applications can use one of two values

## Fast value
peekpoke 16 0xb0010012 0x181

## Slow value (for older PC104 devices)
# peekpoke 16 0xb0010012 0xf0ff

Accessing the MUXBUS

On this board the MUXBUS memory window is available at 0xb0017000. The first 64KB of memwindow space are for using the external bus in 16 bit mode. The next 64KB are for the external bus in 8 bit mode.

Register Map
Offset Bits Access Description
0x0 15:0 Read/Write Address bits 26-11
0x2 15:14 Read/Write
Bus Width
Value Description
0 Reserved
1 32 bit
2 16 bit
3 8 bit
13:12 Read/Write
Data Config
Value Description
0 Normal
1 two-cycle split [1]
3 two-cycle combine [2]
11 Read/Write Auto increment enable [3]
10:0 Read/Write Address bits 10-0
0x4 15:0 Read/Write Data register 1 [4]
0x6 15:0 Read/Write Data register 2[4]
  1. Write 1 16 bit data register to write 2x 8bit cycles.
  2. Write 2 16 bit values to perform 1 32 bit cycle.
  3. When accessing data register 2 the auto increment will step the address by the number of bytes written each time, otherwise writing multiple times will go to the same address.
  4. 4.0 4.1 Data register 1 and 2 both read/write to the same address, but data register 2 can auto increment the address from the number of bytes after each read/write cycle. Data register 1 will ignore 0x2 and will not increment the address.

Within this memory window there are two sets of addresses for 16-bit or 8-bit accesses.

Memory Window Address Bit width
0x0000-0xFFFF 16-bit
0x10000-0x1FFFF 8-bit

At either the 16-bit 0x0000 or 8-bit 0x10000, these start at offset 0 on the bus and access the same address range. The below C example can access 8 and 16 bit registers on the muxbus:

#include <stdio.h>
#include <stdint.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>

static volatile uint16_t *mwin;

void mwinen()
        int mem;
	volatile uint16_t *syscon;
        mem = open("/dev/mem", O_RDWR|O_SYNC);
        syscon = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, mem, 0xb0010000);
        mwin = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, mem, 0xb0017000);

	assert(syscon != NULL);
	assert(mwin != NULL);

	// Set up muxbus timing register.
	syscon[0x12/2] = 0x321;

uint16_t mwinpeek16(uint32_t addr)
	mwin[0x0] = addr >> 11; // This register contains the upper bits of address
	// Sets the remaining address bits, and requests a 16 bit bus cycle
	mwin[0x2/2] = (addr & 0x7ff) | 0x8000;

	return mwin[0x4/2];

void mwinpoke16(uint32_t addr, uint16_t value)
	mwin[0x0] = addr >> 11;
	mwin[0x2/2] = (addr & 0x7ff) | 0x8000;

	mwin[0x4/2] = value;

uint8_t mwinpeek8(uint32_t addr)
	// 8-bit addr starts 64k into the memory window
	addr = 0x10000 | addr;

	mwin[0x0] = addr >> 11;
	// Similar to the 16 bit transactions, but requests 8 bit bus cycles
	mwin[0x2/2] = (addr & 0x7ff) | 0xc000;

	return mwin[0x4/2];

void mwinpoke8(uint32_t addr, uint8_t value)
	// 8-bit addr starts 64k into the memory window
	addr = 0x10000 | addr;

	mwin[0x0] = addr >> 11;
	mwin[0x2/2] = (addr & 0x7ff) | 0xc000;

	mwin[0x4/2] = value;

int main(int argc, char **argv)
	printf("16-bit read 0x0: 0x%x\n", mwinpeek16(0x0));
	printf("8-bit read 0x0: 0x%x\n", mwinpeek8(0x0));

	return 0;

Note that baseboards may use different offsets for 8-bit and 16-bit off-board peripherals such as PC104 devices.

SD Interface

The i.MX515 includes an SD controller capable of SD/SDHC/SDXC. Under linux, this is /dev/mmcblk0. Refer to the cpu manual for more information about this controller.


This core is for high speed SPI with auto-CS#. Starts at offset 0x40 on the this series. Chip select #0 is typically used for onboard spiflash. Chip select #1 is used for offboard spiflash. The last 2 chip selects are always available on the Cavium series boards.

The SPI controller is an FPGA core which is accessed using spictl. The simplest method for communication is calling spictl through bash:

# Read 32 bytes from LUN1
spictl --lun=1 --readstream=32

# Write Hello (68:65:6c:6c:6f)
spictl --lun=1 --writestream=68:65:6c:6c:6f


ts4800:~# spictl --help
Technologic Systems SPI controller manipulation.

General options:
-c | --clock=frequency    SPI clock frequency
-e | --edge=value         set clock edge (positive for > 0, negative for < 0)
-w | --writestream=data   write colon delimited hex octets to SPI
-d | --readwrite=data     write colon delimited hex octets to SPI while reading to stdout
-r | --readstream=bytes   read specified number of bytes from SPI to stdout
-o | --holdcs             don't de-assert CS# when done
-l | --lun=id             Talk to specified chip number
-s | --server=<port>      Daemonize and run as server listening on port
-p | --port=<host><:port> Talk to spictl server
hex octets are hexadecimal bytes. for example,
this command reads 32 bytes of CS#1 SPI flash from address 8192:
./spictl -l 1 -w 0B:00:20:00:00 -r 32

The spictl utility can also run as a TCP server which lets you easily access SPI in your application. To start the tcp server on port 7755:

spictl --server=7755

The data stream packet to a spictl server consists of opcodes and operands. Each opcode is one byte long and may encode part or all of the operand. Some opcodes specify that additional bytes of data follow to contain the remainder of the operands.

There are four opcodes encoded in the two msb of the opcode byte:

    • The chip number is encoded in the two LSB.
      • 00 = CS#0
      • 01 = CS#1
      • 10 = CS#2
      • 11 = CS#3
    • If Bit 5 is set, OPCODE = ASSERT CHIP SELECT.
    • Then If Bit 3 is set, Bit 2 is the new SPI edge to use (1 = positive edge, 0 = negative edge). Also, two additional bytes follow as operands. These two bytes are a big-endian encoded clock value. This value multiplied by 2048 is the SPI clock frequency to use. If Bit 5 is clear, OPCODE = DE-ASSERT CHIP SELECT
    • The number of bytes to read must be a power of two, encoded in the 6 lsb. These six bits represent the number to raise 2 to the power of to get the length. So,
      • 00_0000 = 1 byte
      • 00_0001 = 2 bytes
      • ...
      • 00_1100 = 4096 bytes
    • The number of bytes to write is encoded in the same manner as for a READ opcode. After the opcode byte, the number of bytes to write follows as the operands.
    • This opcode encodes identically as the WRITE opcode. However it specifies that bytes are to be READ as well as written.

You can also use the spictl --server=<port> and run a second invokation of spictl with --port=<port> to have the second instance act as a client to the server. You can then use tcpdump to see the exact tcp packets being sent back and forth for various operations.

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

Offset Access Bit(s) Description
0x40 Read Only 15 SPI MISO state
Read/Write 14 SPI CLK state
Read/Write 13:10 Speed - 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
0x42 Read Only 16:0 Previous SPI read data from last write
0x44 N/A 16:0 Reserved
0x46 N/A 16:0 Reserved
0x48 Read/Write 16:0 SPI read/write with CS# to stay asserted
0x4a Read Only 16:0 SPI pipelined read with CS# to stay asserted
0x4c Read/Write 16:0 SPI Read/Write with CS# to deassert post-op
0x4e N/A 16: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. For speed == 0, SPI clock polarity/skew must be set from the PLL phase adjust registers in the syscon block.

Where the base clock is 75Mhz (extended temp alters this to 50Mhz), speed settings break down as follows:

 0 - 75Mhz (/1)
 1 - 37.5Mhz (/2)
 2 - 18.75Mhz (/4)
 3 - 12.5Mhz (/6)
 4 - 9.375Mhz (/8)
 5 - 7.5Mhz (/10)
 6 - 6.25Mhz (/12)
 7 - 5.36Mhz (/14)
 8 - 4.68Mhz (/16)
 9 - 4.17Mhz (/18)
 15 - 2.5Mhz (/30)
 19 - 1.97MHz (/38)
 31 - 1.21MHz (/62)

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. This register is an appropriate target address for SBUS burst reads.


The registers listed below are all 16 bit registers and must be accessed with 16 bit reads and writes. This register block appears at base address 0xb0010000. You can use the peekpoke command as a simple way of manipulating these registers:

peekpoke 16 0xb0010000

This will return "0x4800" which matches the model ID register below.

Offset Bits Access Usage
0x0 15:0 Read Only Model ID register. Reads 0x4800 on TS-4800
0x2 15 Read/Write Reset FPGA
14 Reserved (write 0)
13 SD Power (1 = on)
12 Boot mode
11 Baseboard 12MHz clk (overrides DIO 3)
10 Reserved
9 CAN1 IRQ enable
8 Reserved
7:4 Read Only board submodel - 0x0 on production TS-4800
3:0 Read Only FPGA revision
0x4 15:0 Read Only 32-bit 72MHz free running counter (16 LSBs)
0x6 15:0 Read Only 32-bit 72MHz free running counter (16 MSBs)
0x8 15:0 Read Only 32-bit 1MHz free running counter (16 LSBs)
0xa 15:0 Read Only 32-bit 1MHz free running counter (16 MSBs)
0xc 15:0 Read Only 16-bit random number
0xe 15:2 Read Only Reserved
1:0 Write Only Watchdog feed register (write only)
0x10 15 Read/Write Green LED (1 = on)
14 Red LED (1 = on)
13 IRQ7 enable (1 = on)
12 IRQ6 enable (1 = on)
11 IRQ5 enable (1 = on)
10 Reserved
9 Sound Codec enable (1 = on)
8 Motor Controller enable (1 = on)
7 SPI enable (1 = on)
6 TOUCHSCREEN enable (1 = on)
5 XUART5 enable (1 = on)
4 XUART4 enable (1 = on)
3 XUART3 enable (1 = on)
2 XUART0 enable (1 = on)
1 CAN1 enable (1 = on)
0 CAN0 enable (1 = on)
0x12 15:0 Read/Write MUXBUS configuration register
0x14 15:4 Read Only Reserved
3 Read/Write Lattice tagmem clock
2 Read/Write Lattice tagmem serial-in
1 Read/Write Lattice tagmem CSn
0 Read Only Lattice tagmem serial-out
0x16 15 Read/Write Input counter1 enable
14 Input counter0 enable
13 System-on-Module RS422 enable (xuart0 = DIO_13)
12 System-on-Module RS422 Enable (xuart4 = DIO_13)
11 Reserved
10:6 PLL phase (set by TS-BOOTROM) (RW)
5 Mode3 latched bootstrap bit (RO)
4 Reset switch enable (1 = auto reboot when dio_i[9] == 0) (RW)
3:2 Scratch Register (used by bootrom)
1 Mode2 latched bootstrap bit (RO)
0 Mode1 latched bootstrap bit (RO)
0x1a 15:0 Read Only Input counter0
0x1c 15:0 Read Only Input counter1
0x1e 15:0 Read/Write Test Register
0x20 15:0 Read Only DIO input data for DIO 15(MSB)-0(LSB)
0x22 15:0 Read/Write DIO output data for DIO 15(MSB)-0(LSB)
0x24 15:0 Read/Write DIO direction for DIO 15(MSB)-0(LSB) (1 = output)
0x28 15:0 Read Only DIO input data for DIO 31(MSB)-16(LSB)
0x2a 15:0 Read/Write DIO output data for DIO 31(MSB)-16(LSB)
0x2c 15:0 Read/Write DIO direction for DIO 31(MSB)-16(LSB) (1 = output)
0x30 15:0 Read Only DIO input data for DIO 47(MSB)-32(LSB)
0x32 15:0 Read/Write DIO output data for DIO 47(MSB)-32(LSB)
0x34 15:0 Read/Write DIO direction for DIO 47(MSB)-32(LSB) (1 = output)
0x38 15:8 Read Only Reserved
7:0 Read Only DIO input data for DIO 55(MSB)-48(LSB)
0x3a 15:8 Read Only Reserved
7:0 Read/Write DIO output data for DIO 55(MSB)-48(LSB)
0x3c 15:8 Read/Write Reserved
7:0 Read/Write DIO direction for DIO 55(MSB)-48(LSB) (1 = output)

TS-8100 Register Map

All of these registers are intended for 16 bit access. You can find this range at 0xb0011000. You must first set the MUXBUS configuration register before accessing this range. For example, to read the board ID:

peekpoke 16 0xb0010012 0x181 # Set MUXBUS configuration
peekpoke 16 0xb0011000 # Read Board ID register (0x0)

Offset Bits Access Description
0x0 15:0 Read Only Board ID (0x8100)
0x2 3:0 Read Only PLD revision
7:4 Read/Write Value to control PWM for LCD contrast
8 Read/Write TS-8100 USB Reset
9 Read/Write Controls ISA_RESET on the PC104 bus
10 Read/Write Enables a 14.3MHZ clock on the PC104 bus (B30) and the PLD (default 1)
11 Read/Write Enables the RS232 transceiver (default 1)
12 Read/Write Toggles 5V to the LCD header pin 1 (LCD_5V)
13 Read/Write Enable CAN1 standby
14 Read/Write Enable CAN2 standby
15 Read/Write Enables the PWM output for the contrast value
0x4 7:0 Read/Write DIO output data (header odd pins, bit 0 is pin 1)
13:8 Read/Write PC104 header A21:A16 output data
15:14 Read/Write PC104 header B12:B11 output data
0x6 7:0 Read/Write LCD pins 14:7 output data
8 Read/Write LCD Header pin 6 output data
9 Read/Write LCD Header pin 3 output data
10 Read/Write LCD Header pin 5 output data
11 Read/Write AVR MOSI
12 Read/Write AVR SCLK
13 Read/Write AVR RESET
14:15 N/A Reserved
0x8 7:0 Read/Write DIO Header odd pins 15:1 data direction
13:8 Read/Write PC104 A21:A16 data direction
15:14 Read/Write PC104 B12:B11 data direction
0xa 7:0 Read/Write LCD pins 14:7 data direction
8 Read/Write LCD Header pin 6 data direction
9 Read/Write LCD Header pin 3 data direction
10 Read/Write LCD Header pin 5 data direction
15:11 N/A Reserved
0xc 7:0 Read Only DIO Header dd pins 15:1 input data
13:8 Read Only PC104 A21:A16 input data
15:14 Read Only PC104 B12:B11 input data
0xe 7:0 Read Only LCD header pins 14-7 input data
8 Read Only LCD Write/Read (pin 6) input data
9 Read Only LCD Register Select (pin 3) input data
10 Read Only LCD Enable (pin 5) input data
11 Read/Write AVR MISO
15:12 N/A Reserved


The TS-4800 CPU has 3 UARTS brought out. The XUARTs compliment this by adding 4 additional UARTS with one that can be remapped for RS422. Our XUART core will support up to 8 XUARTs total so contact us for FPGA customization options if you require more, or see the #FPGA Programming section.

These are using an FPGA core we call XUARTs which have some benefits for embedded applications over traditional UARTs. XUARTs have a shared 4KB RX buffer which allows low CPU usage without losing data. The XUARTs also allow arbitrary baudrates in a simple format.

XUARTs also include an automatic TX enable for RS485 ports. This is configured by accessing the #Syscon registers. Whenever the UART is writing the RX side will automatically be silenced so characters are not echoed back.

XUARTs are different than traditional UARTs in a few ways. The XUARTs cannot set the baud rate or mode cannot be set using ioctls either manually or using utilities like setserial or stty. Manually sending a break command requires using the raw TCP api. The baud rate and mode are instead set when the XUART is initialized. For example:

eval $(xuartctl --server --port 0 --mode 8n2 --speed 9600 2>&1); ln -sf $ttyname /dev/ttyxuart0
eval $(xuartctl --server --port 1 --mode 8n1 --speed 115200 2>&1); ln -sf $ttyname /dev/ttyxuart1
eval $(xuartctl --server --port 2 --mode 8n1 --speed 38400 2>&1); ln -sf $ttyname /dev/ttyxuart2

You can specify different ports and link locations for additional UARTs if they are enabled in your FPGA. Once the /dev/ttyxuart# device is created you can treat this as a traditional UART. You can use applications like picocom, pppd, minicom, and so on directly on the UART as expected.

For using either the CPU or FPGA serial ports these are great resources for serial programming with Linux:

For more detail on XUARTs, see the Xuartctl page.

Port RX TX TX Enable Notes
ttymxc0 CN2_95 CN2_93 N/A
XUART0 CN2_78 CN2_80 CN1_67 (DIO_12) Toggle with xuart0_on and xuart0_off.
ttymxc1 CN2_84 CN2_82 N/A
ttymxc2 CN2_88 CN2_86 N/A
XUART1 CN2_92 CN2_90 N/A Toggle with xuart3_on xuart3_off
XUART2 CN2_96 CN2_94 CN1_63 (DIO_14) Toggle with xuart4_on xuart4_off.
XUART3 CN2_100 CN2_98 N/A Toggle with xuart5_on xuart5_off
XUART0 CN1_65 N/A N/A This is RX only. Must be remapped by toggling syscon offset 0x16 bit 12.
Note: The xuart#_on/off functions are not needed in REV 7 of the FPGA. See the #FPGA section for more details.

To remap the XUART0 from CN2_78-CN2_80 to RX Only on CN1_65 you must set this bit in the syscon:

peekpoke 16 0xb0010016 0x1023
Port Type RX (or 485 +) TX (or 485 -) Notes
ttymxc0 RS232 COM1 header pin 2, DB9 pin 2 COM1 header pin 3, DB9 pin 3 Only with "Console" jumper on
XUART0 RS485 COM1 header pin 1, DB9 pin 1 COM1 header pin 6, DB9 pin 6 Toggle with xuart0_on and xuart0_off
ttymxc1 RS232 COM1 header pin 2, DB9 pin 2 COM1 header pin 3, DB9 pin 3 Only with "Console" jumper off
ttymxc2 RS232 COM1 header pin 8, DB9 pin 8 COM1 header pin 7, DB9 pin 7
XUART1 RS232 COM2 header pin 2 COM2 header pin 3 Toggle with xuart3_on xuart3_off
XUART2 RS485 COM2 header pin 1 COM2 header pin 6 Toggle with xuart4_on xuart4_off
XUART3 RS232 COM3 header pin 2 COM3 header pin 3 Toggle with xuart5_on xuart5_off
XUART0 RS485/RS422 COM2 header pin 4 COM2 header pin 9 This is RX only. Must be remapped by toggling syscon offset 0x16 bit 12.


USB Host

The TS-4800 USB Host controller supports USB 2.0 at 480Mbps.


The watchdog is manipulated via the ts4800ctl utility. The default INITRD linuxrc autofeeds the watchdog by daemonizing and feeding it in the background via userspace. It can be armed in 3 modes (0 - .4s, 1- 2.7s, 2 - 10.8s). It can be either auto-fed from a background process that continually feeds the watchdog while running (--autofeed option), or via a /dev/watchdog UNIX named pipe which receives single ASCII characters which are written to feed it from another application.

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

Watchdog by default comes out of reset armed for .338 seconds. TS-BOOTROM firmware feeds for 10.824 and OS code has 10.824 seconds to take over.

You can feed this from your own application by touching the feed register:

#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>

int main()
        int mem;
        volatile uint16_t *syscon;

        mem = open("/dev/mem", O_RDWR|O_SYNC);
        syscon = mmap(0,

        for(;;) {
                // This feeds the watchdog for 10s.
                syscon[0xe/2] = 2;

        return 0;

To remap the XUART0 from the RS485 on COM1 to RS485 on COM2 for RS485/RS422 RX:

peekpoke 16 0xb0010016 0x1023


The XNAND access is implemented in userspace in conjunction with NBD (network block device). You may want to refer to the nandctl page which will show more advanced usage, but by default the linuxrc script will mount the sd card with the following layout.

 /dev/nbd0 - whole disk device of XNAND drive
 /dev/nbd1 - 1st partition (kernel partition)
 /dev/nbd2 - 2nd partition (EXT2 initrd)
 /dev/nbd3 - 3rd partition (252MByte mini Debian EXT3 filesystem)
 /dev/nbd4 - 4th partition (unused)
Note: NBD devices do not report size correctly. If you are formatting a partition or using dd you will need to specify the size.

External Interfaces

ADC Header

The Analog to Digital Converter consists of a 4-channel 16 bit sigma-delta converter and two, 2-channel analog switches. These are configured to allow input and conversion on two differential channels and 4 single ended channels. The 6-channel Analog to Digital signals are contained on connector HD5 which is a 16 pin (2x8) 0.1" spacing header. The connector layout and the signals carried by each pin are defined below. The input range for the differential input channels is 0- 2 VDC, and the input range on the single-ended channel is nominally 0-10 VDC.

Pinout Header
Pin Type Signal
1 Single ended Channel 6
3 Single ended Channel 5
5 Single ended Channel 4
7 Single ended Channel 3
9 N/A Not connected
10 N/A GND
11 Differential Channel 2-
12 Differential Channel 2+
13 N/A GND
14 Differential Channel 1-
15 Differential Channel 1+
16 N/A GND


Refer to the ADC Core section for more information on software support.

COM Headers

The TS-8100 includes 3 2x5 0.1" pitch COM headers that feature RS232, RS485, and CAN ports. These follow a different pin layout which corresponds with a standard 10 pin header standard for UARTs. The RC-DB9 is available to convert these ports to a DB9.

Pinout Header
COM1 header [1]
Pin Name
1 XUART0 RS485+[2]
2 RS232 RXD [3]
3 RS232 TXD


6 XUART0 RS485- [2]
7 ttymxc2 RS232 TXD
8 ttymxc2 RS232 RXD
10 Unused


COM2 header
Pin Name
1 XUART2 RS485+ [5][6]
2 XUART1 RS232 RXD[7]
3 XUART1 RS232 TXD[7]
4 XUART0 RS422+ [8]
6 XUART2 RS485- [6] [5]
7 Unused
8 Unused
9 XUART0 RS422- [8]
COM3 header
Pin Name
1 Unused
2 XUART3 RS232 RXD [9]
3 XUART3 RS232 TXD [9]
4 CAN2_H[10]
6 Unused
7 Unused
8 XUART3 RS232 CTS[9]
9 CAN2_L [10]
10 Unused
  1. This header is brought out to both a 2x10 header and a #DB9.
  2. 2.0 2.1 Toggle with xuart0_on and xuart0_off
  3. CONSOLE RS232 RXD with "Console Enable" jumper on or ttymxc1 RS232 RXD with "Console Enable" jumper off
  4. CONSOLE RS232 TXD with "Console Enable" jumper on or ttymxc1 RS232 TRXD with "Console Enable" jumper off
  5. 5.0 5.1 The second RS485 transceiver requires OP-CAN2-485 to be ordered with the board
  6. 6.0 6.1 Toggle with xuart4_on and xuart4_off
  7. 7.0 7.1 Toggle with xuart3_on and xuart3_off
  8. 8.0 8.1 This is an RS485 port with RX only. This port can be enabled with xuart0_on. This also must be remapped from COM1 with "peekpoke 16 0xb0010016 0x1023"
  9. 9.0 9.1 9.2 Toggle with xuart5_on xuart5_off
  10. 10.0 10.1 The second CAN transceiver requires OP-CAN2-485 to be ordered with the board


COM1 header [1]
Pin Name
1 XUART0 RS485+
2 RS232 RXD [2]
3 RS232 TXD


6 XUART0 RS485-
10 Unused


  1. This header is brought out to both the DB9 and10 pin header.
  2. CONSOLE RS232 RXD with "Console Enable" jumper on or XUART1 RS232 RXD with "Console Enable" jumper off
  3. CONSOLE RS232 TXD with "Console Enable" jumper on or XUART1 RS232 TRXD with "Console Enable" jumper off

DIO header

The TS-8100 includes a 2x8 0.1" pitch header with 8 DIO, I2C, and SPI. Most DIO on this header are rated for 3.3V and are not tolerant of 5V IO. The only exception is SPI_MOSI which is 5V tolerant. The DIO on this baseboard can be accessed by manipulating the TS-8100 Register Map, or using tsctl.

Pinout Header
Pin Name Notes
1 8100_DIO_1 Pulled high by R124
2 Ground
3 8100_DIO_3 Pulled high by R123
5 8100_DIO_5 Pulled high by R122
7 8100_DIO_7 pulled high by R121
9 8100_DIO_9 Pulled high by R120
11 8100_DIO_11 Pulled high by R119
13 8100_DIO_13 Pulled high by R118
15 8100_DIO_15 Pulled high by R117
16 CPU_3.3V


LCD Header

The LCD header is designed around compatibility with the LCD-LED: Alphanumeric 2x24 LCD. These I/O are manipulated by accessing the register map. Connector CN8 is a 14 pin (2x7) 0.1" spacing header.

Pinout Header
Pin Name
1 LCD_5V [1]
2 Ground
7 LCD_D1
8 LCD_D0
9 LCD_D3
10 LCD_D2
11 LCD_D5
12 LCD_D4
13 LCD_D7
14 LCD_D6


  1. Provides up to 1400mA
WARNING: LCD_D0 thru LCD_D7 are 5V tolerant. LCD_WR#, LCD_RS, and LCD_EN are not.

The LCD_5V pin can provide up to 1400mA, but this is a shared 5V rail and will depend on your power supply and what other devices are using that rail.

This example project allows you to pipe in data separated by newlines, or you can call the application with arguments to draw the two lines. For example:

./lcd Technologic Systems

Will write this to the screen:

LCD LED example.jpg
/* LCD 2x24 character example code
 * To compile, copy to the board and run:
 * 	gcc lcd.c -o lcd  */
#include <stdio.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
void lcd_init(void);
void lcd_wait(void);
void lcd_command(unsigned int cmd);
void lcd_writechars(unsigned char *dat);
// These are nanosecond delays
#define SETUP   800
#define PULSE   1600
#define HOLD    800
// The mpeek/mpoke functions are specific to the TS-4800
static volatile uint16_t *mwin;
void mwinen()
        int mem;
	volatile uint16_t *syscon;
        mem = open("/dev/mem", O_RDWR|O_SYNC);
        syscon = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, mem, 0xb0010000);
        mwin = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, mem, 0xb0017000);
	assert(syscon != NULL);
	assert(mwin != NULL);
	// Set up muxbus timing register.
	syscon[0x12/2] = 0x321;
uint16_t mpeek16(uint32_t addr)
	mwin[0x0] = addr >> 11; // This register contains the upper bits of address
	// Sets the remaining address bits, and requests a 16 bit bus cycle
	mwin[0x2/2] = (addr & 0x7ff) | 0x8000;
	return mwin[0x4/2];
void mpoke16(uint32_t addr, uint16_t value)
	mwin[0x0] = addr >> 11;
	mwin[0x2/2] = (addr & 0x7ff) | 0x8000;
	mwin[0x4/2] = value;
void lcd_init(void) {
    uint16_t out;
    // Data lines to inputs, control lines to outputs
    mpoke16(0xa, 0x700);
    out = mpeek16(0x6);
    // Set LCD_EN and LCD_RS low
    out &= ~(0x600);
    // Set LCD_WR high
    out |= 0x100;
    mpoke16(0x6, out);
    lcd_command(0x38); // two rows, 5x7, 8 bit
    lcd_command(0x38); // two rows, 5x7, 8 bit
    lcd_command(0x38); // two rows, 5x7, 8 bit
    lcd_command(0x6); // cursor increment mode
    lcd_command(0x1); // clear display
    lcd_command(0xc); // display on, blink off, cursor off
    lcd_command(0x2); // return home
void lcd_wait(void) {
    uint16_t ddr, out, in;
    int i, dat, tries = 0;
    struct timespec dly;
    dly.tv_sec = 0;
    mpoke16(0xa, mpeek16(0xa) & 0xff00);
    out = mpeek16(0x6);
    do {
        // step 1, apply RS & WR
        out |= 0x100; // de-assert WR
        out &= ~0x200; // de-assert RS
        mpoke16(0x6, out);
        // step 2, wait
        dly.tv_nsec = SETUP;
        nanosleep(&dly, NULL);
        // step 3, assert EN
        out |= 0x400;
        mpoke16(0x6, out);
        // step 4, wait
        dly.tv_nsec = PULSE;
        nanosleep(&dly, NULL);
        // step 5, de-assert EN, read result
        in = mpeek16(0xe) & 0xff;
        out &= ~0x400; // de-assert EN
        mpoke16(0x6, out);
        // step 6, wait
        dly.tv_nsec = HOLD;
        nanosleep(&dly, NULL);
    } while (in & 0x80 && tries++ < 1000);
void lcd_command(unsigned int cmd) {
    int i;
    uint16_t out;
    struct timespec dly;
    dly.tv_sec = 0;
    // Set port A to outputs
    mpoke16(0xa, mpeek16(0xa) | 0x00ff);
    out = mpeek16(0x6);
    // step 1, apply RS & WR, send data
    out &= 0xff00;
    out |= (cmd & 0xff);
    out &= ~(0x300); // de-assert RS, assert WR
    mpoke16(0x6, out);
    // step 2, wait
    dly.tv_nsec = SETUP;
    nanosleep(&dly, NULL);
    // step 3, assert EN
    out |= 0x400;
    mpoke16(0x6, out);
    // step 4, wait
    dly.tv_nsec = PULSE;
    nanosleep(&dly, NULL);
    // step 5, de-assert EN 
    out &= ~0x400;
    mpoke16(0x6, out);
    // step 6, wait 
    dly.tv_nsec = HOLD;
    nanosleep(&dly, NULL);
void lcd_writechars(unsigned char *dat) {
    int i;
    uint16_t out = mpeek16(0x6);
    struct timespec dly;
    dly.tv_sec = 0;
    do {
        // set data lines to outputs
        mpoke16(0xa, mpeek16(0xa) | 0x00ff);
        // step 1, apply RS & WR, send data
        out &= 0xff00;
        out |= *dat++;
        out |= 0x200; // assert RS
        out &= ~0x100; // assert WR
        mpoke16(0x6, out);
        // step 2
        dly.tv_nsec = SETUP;
        nanosleep(&dly, NULL);
        // step 3, assert EN
        out |= 0x400;
        mpoke16(0x6, out);
        // step 4, wait 800 nS
        dly.tv_nsec = PULSE;
        nanosleep(&dly, NULL);
        // step 5, de-assert EN 
        out &= ~0x400;
        mpoke16(0x6, out);
        // step 6, wait
        dly.tv_nsec = HOLD;
        nanosleep(&dly, NULL);
    } while(*dat);
 /* This program takes lines from stdin and prints them to the
 * 2 line LCD connected to the TS-8100/TS-8160 LCD header.  e.g
 *    echo "hello world" | lcdmesg
 * It may need to be tweaked for different size displays
int main(int argc, char **argv)
    int i = 0;
    if (argc == 2) {
    if (argc > 2) {
        lcd_command(0xa8); // set DDRAM addr to second row
    if (argc >= 2) return 0;
    while(!feof(stdin)) {
        unsigned char buf[512];
        if (i) {
            // XXX: this seek addr may be different for different
            // LCD sizes!  -JO
            lcd_command(0xa8); // set DDRAM addr to second row
        } else {
            lcd_command(0x2); // return home
        i = i ^ 0x1;
        if (fgets(buf, sizeof(buf), stdin) != NULL) {
            unsigned int len;
            buf[0x27] = 0;
            len = strlen(buf);
            if (buf[len - 1] == '\n') buf[len - 1] = 0;
    return 0;

PC104 Header

The PC/104 connector consists of two rows of pins labeled A and B, the numbering of of which is shown below. The signals for the PC-104 are generated by the MAX240 PLD located on the baseboard. It converts the MUXBUS signals from the dual 100-pin System-on-Module TS-STOCKET interface bus.

Any of the IO on this board labelled DIO_ can be controlled through manipulation of the TS-8100 registers.

You can also drive these DIO to manually manipulate the PC104 address to make peripherals usable that require a higher range of address than provided by the default address space of the MUXBUS.

Pin Name Pin Name
A1 BUS_BHE# B1 Ground
A3 AD_06 B3 5V
A4 AD_05 B4 AD_08
A5 AD_04 B5 CPU_3.3V
A6 AD_03 B6 Not connected
A7 AD_02 B7 Not connected
A8 AD_01 B8 Not connected
A10 ISA_WAIT# B10 Ground
A11 Ground B11 DIO_B11
A12 Not connected B12 DIO_B12
A13 Not connected B13 ISA_LOW#
A14 Not connected B14 ISA_IOR#
A15 Not connected B15 Not connected
A16 DIO_A16 B16 Not connected
A17 DIO_A17 B17 AD_09
A18 DIO_A18 B18 AD_10
A19 DIO_A19 B19 Not connected
A20 DIO_A20 B20 AD_12
A21 DIO_A21 B21 ISA_IRQ7
A24 ISA_ADD7 B24 Ground
A25 ISA_ADD6 B25 AD_11
A26 ISA_ADD5 B26 AD_13
A27 ISA_ADD4 B27 AD_14
A28 ISA_ADD3 B28 AD_15
A29 ISA_ADD2 B29 5V
A30 ISA_ADD1 B30 ISA 14.3 MHZ
A31 ISA_ADD0 B31 Ground
A32 Ground B32 Ground
TS-8100-PC104 Pinout.png
WARNING: Most of the pins on the PC104 bus are only 3.3V tolerant. Refer to the schematic for more details.

Push Button

The TS-8100 includes a push button which is connected to DIO 9 on the System-on-Module. By default this DIO is configured to reset the processor immediately when pushed. You can use this as a normal input by running:

ts4800ctl --resetswitchoff

From Debian you can use the ts.subr file to read this:

root@ts:~# source /initrd/ts.subr 
root@ts:~# ts4800ctl --resetswitchoff
root@ts:~# dio_data_get 0 9 # unpressed
root@ts:~# dio_data_get 0 9 # pressed

SATA Connector

This controller does not support SATA.

Second Ethernet

The TS-8100 supports an optional second Ethernet port through a USB SMSC chip onboard. In linux this creates an eth1 interface. For more information see the #Network Configuration section.

Note: With the second ethernet adapter installed you cannot have the push button.

USB Header

The TS-8100 baseboard includes 2 standard 5-pin USB headers. These can be connected to USB adapters such as the CB-USB-AF5P which allow for simple mounting in custom enclosures.

WARNING: The REV A boards had the white dot denoting pin 1 on the wrong side. The REV B corrects this, but on either board pin 1 is closest to the ethernet connector.
Signals Pin Layout
Pin Signal
1 5V
2 USB -
3 USB +
5 Frame


USB Port

The USB is available on two ports as a USB 2.0 host.

USB Host
Header PIN Name
1 USB_5V
2 USB -
3 USB +

Further Resources

For further support you can go to our Developer Forums here. You can also contact us for more information.

We recommend reading our white papers if they are relevant to your project:

For learning more about Debian:

For Linux programming in general:

Revisions and Changes

TS-8100 PCB Revisions

Revision Changes
  • Initial Release
  • Corrected white dot indicating pin 1 on the USB header
  • Added R41 - improves long term reliability with TS-4200 installed
  • Changed SATA pins on CN2 for compatibility to the TS-4900
  • Updated baseboard id to indicate revision to software
    • This can be read with the carrier board utility ts4200ctl, ts7500ctl, ts4700ctl, or ts4800ctl with the --info option.

Product Notes


Synopsis The REV A board had the white dot indicating pin 1 on the wrong side of the header.
Severity Minor
Class Software bug
Affected TS-8100 REV A
Status Fixed in REV B


On the REV A only board the white dot indicating pin 1 was on the wrong side. This was corrected in REV B, but on both boards the 5V pin is near the ethernet connector. Refer to the #USB Header section for further details.

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.