TS-4800
Product Page | |
Product Images | |
Specifications | |
Documentation | |
---|---|
Schematic | |
Mechanical Drawing | |
FTP Path | |
Processor | |
Freescale i.MX515 Arm® Cortex®-A8 | |
i.MX515 Product Page | |
Reference Manual |
Overview
The TS-4800 is a TS-SOCKET System-on-Module based on the Freescale i.MX515 ARM Cortex-A8 CPU running at 800MHz. The TS-4800 features 10/100 Ethernet, high speed USB host and device (OTG), microSD card, and 256MB XNAND drive.
Getting Started
A Linux workstation is recommended and assumed for development using this documentation. For users in Windows or OSX, we recommend virtualizing Linux using VMWare or similar to make the full power of Linux available. The developer should be comfortable with Linux to work with embedded Linux on the target platform. Most of our platforms run Debian, which is recommended for ease of use if there is no personal distribution preference.
The main reasons that Linux is useful are:
- Linux filesystems on the microSD card can be accessed on the PC.
- More ARM cross-compilers are available.
- If recovery is needed, a bootable medium can be written.
- A network filesystem can be served.
- Builds such as Linux kernel, Buildroot, Yocto, and distro-seed will not work from WSL1/2 on a case-insensitive filesystem.
WARNING: | Be sure to take appropriate Electrostatic Discharge (ESD) precautions. Disconnect the power source before moving, cabling, or performing setup procedures. Inappropriate handling may cause damage to the board. |
The TS-4800 receives power through several pins on the socket connector. Refer to your baseboard documentation or schematics for locating the power in on your board.
Get a Console
Console on the TS-4800 will by default come out of the CPU UART (ttymxc0). You can find more details about where this UART is brought from your baseboard COM ports section. The console will use 8n1, no flow control, and a 115200 baud rate.
You can also telnet to the board with the default network configuration, though this will omit the TS-BOOTROM messages which can be helpful for diagnostics.
Use a null modem cable to connect the ARM system to your workstation. If you do not have a COM port on your system (as many newer systems do not), you can find a USB serial adapter that will bring out RS232.
Console from Linux
There are many serial clients for Linux, but 3 simple ones would be picocom, screen, and minicom. These examples assume that your COM device is /dev/ttyUSB0 (common for USB adapters), but replace them with the COM device on your workstation.
Linux has a few applications capable of connecting to the board over serial. You can use any of these clients that may be installed or available in your workstation's package manager:
Picocom is a very small and simple client.
picocom -b 115200 /dev/ttyUSB0
Screen is a terminal multiplexer which happens to have serial support.
screen /dev/ttyUSB0 115200
Or a very commonly used client is minicom which is quite powerful:
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.
Initrd / Busybox / Fastboot
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
save
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
exit
the boot process will continue. If you are connected through telnet, this will instead open up its own instance of the shell so typing
exit
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:
bit_set() bit_clr() bit_get() gpio_dir_get() gpio_dir_set() gpio_data_get() gpio_data_set() dio_dir_get() dio_dir_set() dio_data_get() dio_data_set() usb_on() usb_off() usb_init() usb_numports() usb_port_devexists() usbload() bbclk_on() bbclk_off() led0() led1() lcd_on() lcd_off() backlite_on() backlite_off() backlite_high() backlite_medium() backlite_low() fix_display() touchscreen_on() touchscreen_off() tpc_up() can0_on() can0_off() can1_on() can1_off() xuart0_on() xuart0_off() xuart3_on() xuart3_off() xuart4_on() xuart4_off() xuart5_on() xuart5_off() spi_on() spi_off() motor_on() motor_off() snd_on() snd_off() irq5_on() irq5_off() irq6_on() irq6_off() irq7_on() irq7_off() spk_on() spk_off() is_ts4800 mxtime() save2sd() save2xnand() save() sd2xnand() xnand2sd() createxnandboot() createxnandroot() get_2nd_mac() tshelp() sdmount() help()
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.
System 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 255.255.255.0 subnet mask)
ifconfig eth0 192.168.0.50
# Set a specific subnet
ifconfig eth0 192.168.0.50 netmask 255.255.0.0
# Configure your route. This is the server that provides your internet connection.
route add default gw 192.168.0.1
# Edit /etc/resolv.conf for your DNS server
echo "nameserver 192.168.0.1" > /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
dhclient
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 address 192.168.0.50 netmask 255.255.255.0 gateway 192.168.0.1 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 start) /sbin/ts4700ctl --redledon ## If you are launching a daemon or other long running processes ## this should be started with # nohup /usr/local/bin/yourdaemon & ;; stop) /sbin/ts4700ctl --redledoff ;; *) echo "Usage: customstartup start|stop" >&2 exit 3 ;; esac 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.
Creating a Custom Startup Splash
Our splash screens are generated by writing the raw pixel format directly to the screen. For our touchscreens this is RGB565. To generate this first create a PNG of your logo. You can use ffmpeg either on the board installed from the apt repositories, or from another desktop system. When designing your splash screen keep in mind that it will compress much better in this format when there are solid colors. Our default splash has our logo in the center with a solid black background at 800x480 which is about 3kb. If the file is too large you may have to reformat the disk to expand the size of the initrd.
# Replace image.png with your filename
ffmpeg -vcodec png -i image.png -vcodec rawvideo -f rawvideo -pix_fmt rgb565 splash-800x480
gzip splash-800x480
In the initrd you will find a splash-<resolution>.gz which is loaded automatically on startup. The actual resolution of the PNG should match the size of your display as well. The resolution is varied based on which display you are using:
Baseboard | Resolution |
---|---|
TS-TPC-8390 | 800x480 |
TS-TPC-8400 | 640x480 |
TS-TPC-8900 | 800x600 |
Backup / Restore
While all of our products ship with images pre-loaded in to any supplied media, there are many situations where new images may need to be written. NOTE: 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 image or re-image many stock boards when moving your product into production. You can find more information about this method with an example script on the USB-Blaster page linked here.
You can alternately use more direct methods of writing either SD or eMMC boot images, these methods (detailed below) are a good means of returning an R&D device to a known-good working software state, with the shipping images linked in their applicable section below.
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.
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:
mount
# 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
Kernel
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
XNAND
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...
done
Creating root filesystem...
done
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.
Editors
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.
Compilers
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.
Debuggers
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.
FPU Usage
The ARM Cortex-A8 Neon floating point unit can produce significant performance increases when properly implemented. The example below should be compiled thus:
g++ fpudemo.cpp -o mandel -march=armv7-a -mtune=cortex-a8 -mfpu=neon -ftree-vectorize -mfloat-abi=softfp -ffast-math
// TS-4800 Math demo
// Written by Michael D. Peters for Technologic Systems, Inc.
//
// The goal of this exercise is to demonstrate use of
// floating point math on the TS-4800. We will do this by
// generating a mandelbrot set.
//
// can output a csv in the format x,y,value.
//
// Since the libraries included with the TS-4800 are all compiled
// for soft-float, all mathematics in this project will be
// done without the aid of library functions.
#include <iostream>
using std::cout;
#define XGRIDMAX 1024
#define YGRIDMAX 1024
float scalarX(int, int);
float scalarY(int, int);
// Software entry point.
int main()
{
float grid[XGRIDMAX][YGRIDMAX] = {0};
int a,b,i = 0;
float x0, y0, t, x, y;
// for each cell do:
for(a=0; a < XGRIDMAX; a++){
for(b=0; b < YGRIDMAX; b++){
// setup iteration
x = y = i = 0;
x0 = scalarX(a, XGRIDMAX);
y0 = scalarY(b, YGRIDMAX);
while((x*x + y*y < 4) && i < 1000){
t = x*x - y*y + x0;
y = 2 * x * y + y0;
x = t;
i = i + 1;
}
grid[a][b]=i;
// This will provide a comma delimited list of results
// in case producing a graphic output is desired.
// format: x,y,val,x,y,val,x,y,val,...
// cout << a << ',' << b << ',' << i << ',';
}
}
return 0;
}
// The X position must be a scalar between -2.5 and +1.
// x is the literal pixel position
// maxX is the value to scale against.
float scalarX(int x, int maxX)
{
float x0;
// Error check.
if(maxX == 0) return 0;
// First, the total distance from -2.5 and 1 is 3.5 units.
// We must find the size of the divisions between 0 and 3.5
// for the number of units represented by maxX.
x0 = 3.5/(float)maxX;
// Next, scale x down to the approprate unit.
x0 = (float)x * x0;
// Finally, offset the return value into the expected range.
x0 = x0 - 2.5;
return x0;
}
// The Y position must be a scalar between -1 and 1.
// y is the literal pixel position
// maxY is the value to scale against.
float scalarY(int y, int maxY)
{
float y0;
// Error check.
if(maxY == 0) return 0;
// First, how many units are there between 0 and 2?
y0 = (float) 2/maxY;
// Next, scale y down to y0 size.
y0 = (float) y * y0;
// Finally, shift y0 into range.
y0 = y0 - 1;
return y0;
}
Graphical Development
For drawing interfaces in linux there are a few options. To speak at the lower levels, you can use DirectFB or X11. If you want to draw a simple user interface at a much higher level you should use a graphical toolkit as listed below.
Linux has 3 major toolkits used for developing interfaces. These include QT, GTK, and WxWidgets. For development you may want to build the interface on your desktop PC, and then connect with any specific hardware functionality when it runs on the board. You should also be aware of the versions of GTK, QT, and WX widgets available in the current provided distribution as their APIs can all change significantly between versions. These examples below should help get you started in compiling a graphical hello world application, but for further development you will need to refer to the documentation for the specific toolkits.
Development environment available for Windows, Linux, and Mac. The most common utility used is QT Creator which includes the IDE, UI designer, GDB, VCS, a help system, as well as integration with their own build system. See QT's documentation for a complete list of features. QT can connect with our cross compilers. If you are working with Linux you can use the same cross compiler and connect it with qtcreator. QT also offers professional training from their website. QT has a large range of supported language bindings, but is natively written with C++.
Hello world example
Install the build dependencies
# Make sure you have a valid network connection
# This will take a while to download and install.
apt-get update && apt-get install libqt4-dev qt4-dev-tools build-essential -y
For deployment you only need the runtime libraries. These are divided up by functionality, so use 'apt-cache search' to find the necessary qt4 modules for your project. You can also use the 'libqt4-dev' for deployment, it just may contain more than you need.
This simple hello world app resizes the window when you press the button.
'qtexample.cpp'
#include <QApplication>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPushButton hello("Hello world!");
hello.resize(100, 30);
hello.show();
return app.exec();
}
To compile it:
# Generate the project file
qmake -project
# generate a Makefile
qmake
# build it (will take approximately 25 seconds)
make
This will create the project named after the directory you are in. In this example I'm in /root/ so the binary is 'root'.
# DISPLAY is not defined from the serial console
# but you do not need to specify it if running
# xterm on the display.
DISPLAY=:0 ./root
GTK Development is possible on Windows, Linux, and Mac, but will be significantly easier if done from Linux. Typically you would use the Anjuta IDE which includes IDE, UI designer (GtkBuilder/glade), GDB, VCS, devhelp, and integration with the autotools as a built system. This is only available for Linux. GTK also has a large range of supported bindings, though is natively written in C.
Hello world example
Install the build dependencies
# Make sure you have a valid network connection
# This will take a while to download and install.
apt-get update && apt-get install libgtk2.0-dev pkg-config build-essential -y
For deployment you only need the runtime library 'libgtk2.0-0'. The below example will echo to the terminal the application is run from every time you press the button.
main.c
#include <gtk/gtk.h>
static void hello_cb(GtkWidget *widget, gpointer data)
{
g_print ("Hello World\n");
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *button;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = gtk_button_new_with_label("Hello World");
g_signal_connect(button, "clicked", G_CALLBACK(hello_cb), NULL);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
To compile this:
gcc main.c -o test `pkg-config --cflags --libs gtk+-2.0`
To run this example:
# DISPLAY is not defined from the serial console
# but you do not need to specify it if running
# xterm on the display.
DISPLAY=:0 ./test
Hello world tutorial. This uses the simplest example as it does not use any interface design and creates all widgets from code.
Micah Carrick's GTK/glade tutorial. This will show you how to use the Glade designer (integrated with Anjuta as well) to create an xml description for your interface, and how to load this in your code.
wxWidgets is a cross platform graphics library that uses the native toolkit of whichever platform it is on. It will draw winforms on Windows, and GTK on Linux. While wxWidgets has many tools available for development, Code::Blocks seems the most recommended as it includes wxSmith for designing the user interface, as well as including an IDE and GDB support. The wxWidgets toolkit has some binding support, and is natively written in C++.
Hello world example
Install the build dependencies
# Make sure you have a valid network connection
# This will take a while to download and install.
apt-get update && apt-get install wx2.8-headers wx2.8-i18n libwxgtk2.8-dev build-essential -y
The below example will simply draw a frame that prints 'hello world'.
main.cpp
#include "wx/wx.h"
class HelloWorldApp : public wxApp
{
public:
virtual bool OnInit();
};
DECLARE_APP(HelloWorldApp)
IMPLEMENT_APP(HelloWorldApp)
// This is executed upon startup, like 'main()' in non-wxWidgets programs.
bool HelloWorldApp::OnInit()
{
wxFrame *frame = new wxFrame((wxFrame*) NULL, -1, _T("Hello wxWidgets World"));
frame->CreateStatusBar();
frame->SetStatusText(_T("Hello World"));
frame->Show(true);
SetTopWindow(frame);
return true;
}
To compile this example:
g++ main.cpp `wx-config --cxxflags --libs` -o test
To run this example:
# DISPLAY is not defined from the serial console
# but you do not need to specify it if running
# xterm on the display.
DISPLAY=:0 ./test
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);
if(rc){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
if(rc!=SQLITE_OK){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
}
printf("closing test.db\n");
sqlite3_close(db);
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.
CC=/opt/4800/arm-2008q3/bin/arm-none-linux-gnueabi-gcc
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/
clean:
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@192.168.0.50:/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:
Prerequisites
RHEL/Fedora/CentOS:
yum install ncurses-devel ncurses
yum groupinstall "Development Tools" "Development Libraries"
Ubuntu/Debian:
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.
Getting Started with tsctl
First, download and install the latest version of tsctl as documented in the Getting Started Guide.
In the examples below you can follow along by typing the commands (the portion after the prompt) and expect to see the output below the prompt. Note that while the results should be similar, in some cases you might not see exactly the same results due to variations in execution.
Let's start the tsctl shell:
$ tsctl tsctl 0.93-ts (Dec 7 2012 15:34:29) Type "?" to get context-sensitive help. tsctl>
Let's check that we really have a TS-4800.
tsctl> System ModelId 18432 tsctl>
That doesn't look like 4800! The reason is that the ModelId (and BaseBoardId) commands return 0x4800 (hexadecimal 4800), but the tsctl shell defaults to decimal output. (Note that the command line defaults to hexadecimal!)
We can change to hexadecimal output using the mode command. There is no output from this command.
tsctl> Mode Hex tsctl>
Now let's try again.
tsctl> System ModelId 0x00004800 tsctl>
In addition to the base the output is represented in, you can also change the general format. The tsctl shell defaults to "NoAssign" mode in which only the input or inputs are printed, each on a separate line. The "Assign" mode (which is the default for the command line) prints a descriptive name=value pair for each value output.
tsctl> Mode Assign tsctl>
Now let's re-run the previous command. If your version of tsctl is linked against libreadline, you can use the up-arrow twice to pull the System ModelId command back instead of typing it out.
tsctl> System ModelId System_ModelId_0=0x00004800 tsctl>
Let's run the command again.
tsctl> System ModelId System_ModelId_1=0x00004800 tsctl>
Notice that the name changed slightly. The first part of the name is the class, the second it the field name (which is also the function name in cases where the value is directly returned at the C API level), and the third is a number. This number is the index of the number of times this class function has been called during the current invocation of tsctl.
Let's switch back to NoAssign mode. Although the field names can be useful if you aren't familiar with what the output fields mean, mostly Assign mode is meant for evaluating by the shell to set variables.
tsctl> Mode NoAssign tsctl>
Now let's see what base board we have. Your output will differ depending on what you actually have installed.
tsctl> System BaseBoardId 0x00008200 tsctl>
Let's switch back to decimal output.
tsctl> Mode Dec tsctl>
Now, let's enter the System class onto the command stack so that we don't have to type it repeatedly.
tsctl> System tsctl System>
Note that our first command, "System" was incomplete, which caused tsctl to push it onto the command stack. This can be used to reduce typing when repetitive sequences start with the same partial command. To pop an element off the stack in the shell, enter an empty line.
One feature of libtsctl is the System Map, which contains name/value pairs. The name is any string (8-bit Array) and the value is an integer. First, let's see how many entries are stored in the table.
tsctl System> MapLength 422 tsctl System>
The entries in the table are stored sorted by name (case-insensitively). Entries are used to store DIO names, enumerated values, attributes, and user-defined name/value pairs. If you want to see the entire table you can get each entry one at a time by index number, starting at 1:
tsctl System> MapGet 1 AIO_ADC 1 tsctl System>
The first line contains the name, while the second contains the value.
To get several entries at once let's first put the function name on the command stack
tsctl System> MapGet tsctl System MapGet>
One feature of tsctl is the ability to separate commands by a semi-colon. In the shell (e.g. bash) this requires quoting the semi-colon; in the tsctl shell it does not. Let's get the next ten entries:
tsctl System MapGet> 2;3;4;5;6;7;8;9;10;11 AIO_DAC 2 attrib.8200.Wire.Connector.1.0 1 attrib.8200.Wire.Connector.1.1 1 attrib.8200.Wire.Connector.10.0 2 attrib.8200.Wire.Connector.12.0 2 attrib.8200.Wire.Connector.12.1 2 attrib.8200.Wire.Connector.13.0 2 attrib.8200.Wire.Connector.13.1 2 attrib.8200.Wire.Connector.14.0 2 attrib.8200.Wire.Connector.15.0 2 tsctl System MapGet>
What happened here is that each number got appended as the parameter to the System MapGet function.
Hit enter on an empty line to pop the MapGet function off the command stack.
tsctl System MapGet> tsctl System>
Let's look at some of the attributes available. We can find the name of a connector by number as follows:
tsctl System> MapLookupPartial attrib.Connector.Name. 2 CN2_ tsctl System>
How many connectors are there?
tsctl System> MapLookup attrib.Connector.Count 2 tsctl System>
Depending on your system, you may have more than this! How many pins does connector 1 have?
tsctl System> MapLookup attrib.Connector.1.Pins 100 tsctl System>
Most boards have a green and red LEDs, but the DIO number differs from board to board. Let's see what DIO numbers they are on this board. If the lookup fails, a negative value will be returned.
tsctl System> MapLookup;GREEN_LED;RED_LED;; 64 65 tsctl System>
Note that we used two semi-colons with nothing between them to pop the MapLookup function back off the stack.
What connector is the GREEN_LED on? We can determine this by searching the connector attribute for a value corresponding to the DIO number of GREEN_LED. In the above case, that value is 128. However we can also use GREEN_LED, as the tsctl text interface will automatically translate it to the correct value:
tsctl System> MapLookupPartial attrib.Connector. GREEN_LED 2.8 tsctl System>
Note that in a few rare cases the above lookup will conflict with another attribute and may not work. This is because MapLookupPartial looks for a name starting with the specified string, having the specified value. If we specified a value of "100" we might match "attrib.Connector.1.Pins", "attrib.Connector.2.Pins", or "attrib.Connector.1.8" as these all have a value of 100.
The interpretation of 2.8 is "Connector 2, Pin 8". This is also known as "CN2_8", by combining the name of the connector with the pin number on that connector.
tsctl System> MapLookup CN2_8 64 tsctl System>
NOTE: For widest cross-platform compatibility it is recommended to perform lookups based on connectors, rather than board specific DIO names.
How many DIO are on the board?
tsctl System> MapLookup attrib.DIO.Count 130 tsctl System>
If you have a peripheral board such as a baseboard or PC-104 board with supported DIO, you will see a higher number than this. Note that this number counts all raw, internally addressable DIO, regardless of whether or not they are brought out to pins. As such the actual number of usable DIO will frequently be lower than the number contained in this attribute.
Let's pop the System class from the command stack.
tsctl System> tsctl>
What revision of the FPGA is on the board?
tsctl> System FPGARevision 2 tsctl>
We can read the I2C (TWI) temp sensor on the TS-4200 with tsctl. First let's switch to hexadecimal output for Arrays of bytes.
tsctl> Mode AHex tsctl>
Next, we need to make sure that the pins that are used for TWI are correctly set up, as they are frequently multi-function pins. The easiest way to make sure the pins are set correctly is to lock the function you are going to use. As part of locking the pin will be initialized to the correct function. For some boards (notably the TS-4800) the TWI must always be locked during use as it uses an underlying operating system file to perform its functionaity.
tsctl> TWI Lock 0 0 1 tsctl>
Now verify that the temperature sensor is present at device address 0x49.
tsctl> TWI Read 0x49 1 7 2 TWISuccess 0x01:0x90 tsctl>
If the bytes read back are not 0x01:0x90 (the first value returned is the result code), then the temperature sensor is not present, or there is another problem with the TWI bus. Assuming we get the correct response back, we can next send the commands to start an aquisition, and read back the raw temperature data from the sensor.
tsctl> TWI Write;0x49 1 1 0x40:0x0;0x49 0 0 0x40:0x0;;Read 0x49 1 0 2;; TWISuccess TWISuccess TWISuccess 0x12:0xA0 tsctl>
The value returned can be converted to a temperature as follows:
tempC = (byte[0] * 256 + byte[1]) / 128tempC = (0x12 * 256 + 0xA0) / 128tempC = 37.25C
Note: The ts8160ctl sample application provides the above TWI temp sensor reading functionality using the -t option.
Be sure to unlock any resource when you are done. Best practice is to hold a lock for the minimum amount of time necessary.
tsctl> TWI Unlock 0 0 1 tsctl>
There are also several timing based functions you can use. For instance, you can delay for a specific amount of time. You should see a delay of approximately 1 second (1,000,000 microseconds) when running the command below:
tsctl> Time;Delay 1000000 tsctl Time>
Note that the Delay function does not return a value. If you want to see the actual number of microseconds delayed, use the Wait function instead:
tsctl Time> Wait 1000000;; 1000429 tsctl>
The only difference between Wait and Delay is that the former returns the number of microseconds actually spend waiting, and the latter returns no value. This is an important nuance in the TCP classes, as in certain modes functions that return no data are not called until subsequent functions that do return data are called. This allows for things such as commanding a relay to cycle power on the board issuing the command (so long as it isn't the board interpreting the command), since otherwise the command to restore power would not be sent before power was cut.
Short delay times are generally only useful when using direct access from C. Otherwise, the overhead of parsing the command, sending it across TCP, and interpreting it on the server will be significant in comparison to the amount of time to delay.
We can send and receive CAN messages with tsctl. To do this, it is first necessary to connect the TS-4700 CAN bus to another device which is able to send and receive messages.
First, switch to the CAN class:
tsctl> CAN tsctl CAN>
Next, set the baud rate:
tsctl CAN> BaudSet 1000000 1000000 tsctl CAN>
Now set up the remote device to receive, and then send a message from the TS-4800. The message will used extended addressing, will have an address of 0x1234, and have eight bytes of incrementing data starting with a value of 1:
tsctl CAN> Tx FLAG_EXT_ID 0x1234 1:2:3:4:5:6:7:8 CANSuccess tsctl CAN>
If all goes well Tx should return a positive value to indicate success. Now wait for a CAN message to be received. Our initial call to Tx automatically enabled CAN to transmit, so if we received a message between that time and when we call Rx it will return immediately. TO DO: CAN Rx
Sometimes it may be desirable to try to receive multiple CAN messages with a single command. The RxMulti command specifies a maximum and minimum number of messages to receive before returning. A minimum value of zero indicates to only poll for any messages ready and does not block if no message is waiting. TO DO: CAN RxMulti
Features
CPU
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.
FPGA
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
WARNING: | The soft-reload feature has been deprecated on the TS-4800 FPGA beyond revision 5. embeddedTS does not recommend attempting to use the FPGA soft-load feature on the TS-4800 hardware due to the FPGA's inability to guarantee a reset to the custom logic after the soft-load process. This section left in place for documentation purposes. |
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.
Revision | Changes |
---|---|
8 |
|
7 |
* 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 |
6 |
* 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
WARNING: | The soft-reload feature has been deprecated on the TS-4800 FPGA beyond revision 5. embeddedTS does not recommend attempting to use the FPGA soft-load feature on the TS-4800 hardware due to the FPGA's inability to guarantee a reset to the custom logic after the soft-load process. This section left in place for documentation purposes. |
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.
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.
Baseboard ID
All of our off the shelf baseboards contain a hard wired 3-state 8-input multiplexers. This is not required to implement in custom baseboards, but it can be useful to identify the board in software. During startup of the System-on-Module, 4 DIO are used to obtain the baseboard model ID. The red LED (CN2_06) is state 0, green LED (CN2_08) is state 1, BUS_DIR (CN1_98) is state 2, and BD_ID_DATA (CN1_83) is used for data.
The first 6 lines are used as the six bits that define the baseboard. The last two lines (Y6 & Y7 in the schematic image below) define the bits to indicate the board revision.
You can find example code for accessing the baseboard ID in ts4800ctl. For example, "ts4800ctl -B" will return "baseboard_model=" with the detected baseboard.
For custom baseboards we have reserved the address 42 which will never be used by our standard products.
ID | Baseboard |
---|---|
0 | TS-8200 |
1 | Reserved, do not use |
2 | TS-TPC-8390 |
4 | TS-8500 |
5 | TS-8400 |
6 | TS-8160 |
7 | TS-8100 |
8 | TS-8820-BOX |
9 | TS-8150 |
10 | TS-TPC-8900 |
11 | TS-8290 |
13 | TS-8700 |
14 | TS-8280 |
15 | TS-8380 |
16 | TS-AN20 |
17 | TS-TPC-8920 |
19 | TS-8550 |
20 | TS-TPC-8950 |
22 | TS-8551 |
42 | Reserved for customer use, never used by us |
63 | TS-8200 |
LEDs
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. |
Syscon
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) |
USB
USB Host
The TS-4800 USB Host controller supports USB 2.0 at 480Mbps.
USB OTG
The USB OTG port allows you to use this port as a host or a device. For the OTG options you will need to recompile the kernel and select which USB device to use.
USB Device
The USB Mass Storage Device Linux USB gadget will allow you to use your board as a storage device, like a USB thumb drive, when connected to a host PC. Subsequently, the CPU can access the saved data through the storage element named usb_storage_file.
The IP over USB (A.K.A. USB Ethernet) Linux USB gadget will allow you to connect to your board with a USB cable from a PC like you would with a CAT5 Ethernet cable. You will have access to the board via the TCP/IP connection allowing you to use any networking utility (e.g. ping, ssh, ftp, http, etc).
For Windows, a driver interface configuration file (linux.inf) will need to be downloaded and installed on the host PC. This procedure is described in detail below. The linux.inf file can be downloaded here.
USB Device as Mass Storage
The USB Gadget file storage device will allow you to allow access to a block device (file or otherwise) over USB. To use this functionality, you must first have a block device to give to the driver. In this example I will use a 100MB file on the Debian filesystem.
dd if=/dev/zero of=/root/usbstorage.img bs=1MB count=100
Load the driver with the file as an argument
modprobe g_file_storage file=/root/usbstorage.img
If you now, or are have already connected the USB device cable to a host pc, you should now see the USB device. Like inserting any other usb drive you should now have a new device on your system. From a linux host pc:
[690892.624575] sd 23:0:0:0: Attached scsi generic sg3 type 0 [690892.626160] sd 23:0:0:0: [sdd] 195312 512-byte logical blocks: (99.9 MB/95.3 MiB) [690892.628419] sd 23:0:0:0: [sdd] Write Protect is off [690892.628424] sd 23:0:0:0: [sdd] Mode Sense: 0f 00 00 00 [690892.628911] sd 23:0:0:0: [sdd] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA [690892.644202] sdd: unknown partition table [690892.647287] sd 23:0:0:0: [sdd] Attached SCSI disk
Now on your workstation you can use this device as any other usb storage. As this file contains all zeros, you will need to format it and create a partition/filesystem to be able to store data on it. See the documentation for your workstation for more details. Keep in mind you cannot mount the same block device or file twice so this will not allow you to share your live filesystem over USB.
USB Device as USB Ethernet
In order to use USB Device as USB Ethernet you will first need a new kernel compiled from source. The instructions to compile the kernel can be found here.
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 media.
Once you have added the newly compiled kernel to your image you will now have Ethernet Gadgets driver built in which will allow you to use USB Device as USB Ethernet. The board must be setup prior to connection to a host PC.
Now assign an IP address to the new usb0 interface
ifconfig usb0 192.168.42.20
The IP address in the above example may be any valid IP address, but should typically not be on the same subnet as the Ethernet network on the the board (if connected), or the host computer to which the SBC will be connected.
Connecting Linux Host to the board via IP over USB
Most modern Linux distributions already have all of the required modules (such as usbnet.ko) and utilities installed, so the setup steps are minimal. Simply plug in the board after it has been prepared for IP over USB (see above) and observe that a new interface has been added named usb0 or similar (use dmesg | tail to verify). You can now assign an IP address to that interface with ifconfig (e.g. ifconfig usb0 192.168.42.21) and begin using the TCP/IP connection. To test your connection, use ping 192.168.42.20. You should also be able to login to the SBC using ssh ie. ssh root@192.168.42.40.
Connecting Windows XP Host to the board via IP over USB
An additional driver interface configuration file called linux.inf is required for IP over USB connection to a Windows host. First, download this file onto the Windows PC and remember where you placed it. The linux.inf file can be downloaded here. Next, connect the board and Windows PC with the A to B USB cable (ISB Cable). You should see the "Found New Hardware Wizard". Answer the prompts as follows:
- Select Include this location in the search and choose the location of the driver you downloaded. Finish running the wizard.
- Go to the Control Panel and open "Network Connections". Right-click the new connection (labeled "Linux USB Ethernet/RNDIS Gadget") and click "Rename". Rename it to something useful such as "USB Network".
- Right-click on the newly labeled icon, and select properties.
- Under the properties General tab, select the item labeled Internet Protocol (TCP/IP)
- Select Use the following IP Address, and enter 192.168.42.21.
- Click OK; Click OK
- You may now access the board via the TCP/IP connection. Use ping in the Command Prompt window to verify connectivity (e.g. ping 192.168.42.20).
Note: | The IP address above may be any valid IP address, but must be in the same subnet as the IP address assigned to the board IP over USB connection above. The subnet used should also be different from any other interfaces on the SBC or PC, otherwise strange results may occur. |
SPI
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
Usage:
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:
- OPCODE 0 = CHIP SELECT
- 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 chip number is encoded in the two LSB.
- OPCODE 1 = READ
- 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 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,
- OPCODE 2 = WRITE
- 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.
- OPCODE 3 = READWRITE
- 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.
RTC
The TS-4800 uses an ST Micro M41T00S RTC chip in conjunction with an off-module battery power source to keep accurate system time. This hardware resource is controlled through the use of the Linux-standard hwclock command. More information about hwclock is available in its man page. More information about the hardware battery-backed realtime clock is available from the chip manufacturer's datasheet: http://www.st.com/resource/en/datasheet/m41t00s.pdf
LCD Interface
This interface presents a standard 24 bit LCD video output. The Linux operating system we provide includes drivers for the framebuffer device and X11 support. If you are using our displays the driver is typically set up in the init-xorgenv file in the initrd which will detect which display you are using and set up the resolution accordingly.
See the #Graphical Development section of the manual for more details on examples on drawing to this interface.
For the specifics of this interface for custom baseboard implementations please refer to the CPU manual.
Watchdog
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,
getpagesize(),
PROT_READ|PROT_WRITE,
MAP_SHARED,
mem,
0xb0010000);
for(;;) {
// This feeds the watchdog for 10s.
syscon[0xe/2] = 2;
sleep(5);
}
return 0;
}
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 | ||||||||||
5:4 |
| ||||||||||
3:2 |
| ||||||||||
1:0 |
| ||||||||||
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 |
CAN
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.
PWM/Quadrature (Motor Core)
The TS-4800 FPGA includes our motor core which can be used for quadrature and PWM.
The PWM values are 0-256 for 0%-100% duty cycle.
Address | Bits | Access | Description |
---|---|---|---|
0x0 | 15-0 | Read / Write | PWM Channel 0 |
0x2 | 15-0 | Read/Write | PWM channel 1 |
0x4 | 15-0 | Read/Write | PWM Channel 2 |
0x6 | 15-0 | Read/Write | PWM Channel 3 |
0x8 | 15-0 | Read/Write | PWM Channel 4 |
0xa | 15-0 | Read/Write | PWM Channel 5 |
0xc | 15-0 | Read/Write | PWM Channel 6 |
0xe | 15-0 | Read/Write | PWM Channel 7 |
0x10 | 15-0 | Read Only | Quadrature Count #0 |
0x12 | 15-0 | Read Only | Quadrature Count #1 |
0x14 | 15-0 | Read Only | Quadrature Count #2 |
0x16 | 15-0 | Read Only | Quadrature Count #3 |
0x18 | 15-0 | Read Only | Index Pulse Count #0 |
0x1a | 15-0 | Read Only | Index Pulse Count #1 |
0x1c | 15-0 | Read Only | Index Pulse Count #2 |
0x1e | 15-0 | Read Only | Index Pulse Count #3 |
0x20 | 3-0 | Read/Write | H-Bridge Enable Channel 3-0 (LSB is #0) |
7-4 | Read/Write | Index Pulse Enable Channel 3-0 (LSB is #0) | |
11-8 | Read/Write | Index Pulse Polarity Channel 3-0 (LSB is #0) | |
15-12 | Read/Write | Quadrature Enable Channel 3-0 (LSB is #0) | |
0x22 | 15-8 | Read Only | Quadrature Input Pins 7-0 |
7-0 | Read Only | General Input Pins 7-0 | |
0x24 | 15-4 | N/A | Reserved |
3-0 | Read Only | Overcurrent Status Channel 3-0 |
If the quadrature channels are disabled, quadrature counts become free running upcounters and index pulse counts become edge counters. If overcurrent is tripped, H-bridge enable is automatically temporarily deasserted. This acts as nagative feedback keeping current maxed out at approximately the trip point.
If the Syscon motor core enable bit is set, then the following pin overrides apply:
Connector | Pin | DIO | Motor core usage |
---|---|---|---|
CN1 | 93 | 0 | PWM 0 |
CN1 | 91 | 1 | PWM 1 |
CN1 | 89 | 2 | PWM 2 |
CN1 | 87 | 3 | Quadrature 3 |
CN1 | 77 | 8 | Quadrature 0 |
CN1 | 73 | 9 | Quadrature 1 |
CN1 | 92 | 22 | Quadrature 2 |
CN1 | 90 | 23 | PWM 3 |
CN1 | 88 | 24 | PWM 4 |
CN1 | 84 | 26 | PWM 5 |
CN1 | 82 | 27 | PWM 6 |
CN1 | 80 | 28 | PWM 7 |
SD Controller
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.
XNAND
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. |
XNAND2
XNAND2 is an innovation built upon its XNAND predecessor. This engineering effort was predicated by the NAND industry's falling quality standards and Technologic Systems' dedication to continued superior quality, long lifespan products. XNAND2 introduces a more robust system of redundant, error-corrected data storage, and a whole-device wear leveling system that ensures the longest possible lifespan for NAND media.
Please see our whitepaper on the subject for more detail and information.
To facilitate this new paradigm, a new 'nandctl' binary has been introduced. The features and output of this new utility are detailed in this section.
The command line options for the XNAND2 nandctl are very similar to the original:
# nandctl --help Usage: nandctl [OPTION] ... Technologic Systems NAND flash manipulation. General options: -R, --read=N Read N blocks of flash to stdout -W, --write=N Write N blocks to flash -x, --writeset=BYTE Write BYTE as value (default 0) -i, --writeimg=FILE Use FILE as file to write to NAND -t, --writetest Run write speed test -r, --readtest Run read speed test -n, --random=SEED Do random seeks for tests -z, --blocksize=SZ Use SZ bytes each read/write call -k, --seek=SECTOR Seek to 512b sector number SECTOR -d, --nbdserver=NBDSPEC Run NBD userspace block driver server -I, --bind=IPADDR Bind NBD server to IPADDR -Q, --stats Print NBD server stats -m, --dmesg Print log of NAND activity -f, --foreground Run NBD server in foreground -X, --xnand Use XNAND RAID layer -I, --xnandinit Initialize flash chip for XNAND -L, --listbb List all factory bad blocks -v, --verbose Be verbose (-vv for maximum) -P, --printmbr Print MBR and partition table -M, --setmbr Write MBR from environment variables -h, --help This help When running a NBD server, NBDSPEC is a comma separated list of devices and partitions for the NBD servers starting at port 7525. e.g. "lun0:part1,lun1:disc" corresponds to 2 NBD servers, one at port 7525 serving the first partition of chip #0, and the other at TCP port 7526 serving the whole disc device of chip #1.
The --dmesg command will show a running event log since boot. This is useful for troubleshooting if a failure is suspected.
The --stats command will show a mixture of long-term and short-term statistical data about the NAND chip and the XNAND2 layer over it:
# nandctl --stats nbdpid=146 nbd_readreqs=0 nbd_read_blks=0 nbd_writereqs=0 nbd_write_blks=0 nbd_seek_past_eof_errs=0 xnand2_most_worn=5936 xnand2_spares_used=6 xnand2_spares_remaining=1014 xnand2_total_erases=24156537 xnand2_ecc_fixups=0 xnand2_parity_recovers=0 read_seeks=0 write_seeks=0
This --stats output is helpful for systems where monitoring long-term health is useful.
Stats output definitions:
nbdpid: This is the process id of the nandctl process.
nbd_readreqs: This is the number of read requests received by nandctl since boot.
nbd_read_blks: This is the number of blocks read by the nbd client since boot.
nbd_writereqs: This is the number of write requests received by nandctl since boot.
nbd_write_blks: This is the number of blocks written by the nbd client since boot.
nbd_seek_past_eof_errs: This statistic should always read zero. It's the number of times the OS has asked nandctl to seek past the end of the media.
xnand2_most_worn: This is the number of writes that have been made to the most worn block on the NAND chip over the lifetime of the XNAND2 media.
xnand2_spares_used: This is the number of bad blocks marked by XNAND2 over the lifetime of the XNAND2 media.
xnand2_spares_remaining: This is the number of blocks not currently in active use by the disk block device or the RAID5 like redundant data backup. They are available to participate in wear-leveling activities (along with the blocks used by the disk block device and redundant data).
xnand2_total_erases: This is the number of erases over the lifetime of the XNAND2 media since boot.
xnand2_ecc_fixups: This is the total number of ecc correctable errors XNAND2 has corrected since boot.
xnand2_parity_recovers: This is the total number of blocks XNAND2 has had to recover from parity data.
read_seeks: This is the number of read seeks done since boot.
write_seeks: This is the number of write seeks done since boot.
Upgrading to XNAND2
Replacing XNAND with XNAND2 in a dd image for use in production programing
An XNAND2 formatted NAND device will work on supported products with any bootrom date, whether or not the bootrom supports XNAND2. However, devices can only be booted from the XNAND technology that their bootrom supports. An XNAND2 formatted NAND cannot be booted from a bootrom that only supports XNAND1 and vice versa. This allows for application support of XNAND2, regardless of bootrom support, but only if NAND is not the boot media. Because of this, it is important to update all programming and production processes to support XNAND2. For other production preparation processes that do not re-image the entire device, it is still important to confirm the production process is using the XNAND2 nandctl binary dated October 2016 or later. The following section provides the necessary information to update an existing XNAND1 image with the new XNAND2 nandctl software.
The latest nandctl binary is compatible with both XNAND1 and XNAND2; however it will assume that disk initialization will be targeted at XNAND2 support and it is not possible to force XNAND1 formatting. Because of this, the bootrom should be updated to be compatible with XNAND2 before using '--xnandinit' against a NAND device using the latest nandctl binary. TS-BOOTROMs with a date after October 2016 are compatible with and able to boot XNAND2 devices.
This update will walk through the steps of updating the nandctl binary contained in a customized production image. These steps are not necessary when using our stock image, only if your production process is using an SD or NAND image that has been based on any of our previous shipping images. Note that both SD and NAND images should be updated to properly support XNAND2 in all situations.
To prepare this update, a workstation running linux is necessary, either in a virtual machine or native install. From the workstation, open a terminal window and copy your original production image file to a local working directory (this is done to limit working on production used images). This file will be referenced as diskimg.dd in the following instructions. The latest XNAND2 compatible nandctl binary (link to download is at the top of this section) should also be downloaded in the same working directory.
Next, run the following command:
sudo fdisk -l diskimg.dd
This will produce output like the following:
Disk diskimg.dd: 268 MB, 268435456 bytes
255 heads, 63 sectors/track, 32 cylinders, total 524288 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000
Device Boot Start End Blocks Id System
diskimg.dd1 1 5119 2559+ da Non-FS data
diskimg.dd2 5120 10239 2560 da Non-FS data
diskimg.dd3 10240 524287 257024 83 Linux
The above is the partition table of an XNAND disk. An image for an SD card will have 4 partitions rather than 3, but the same basic layout. The necessary information is the start sector of the second partition with the Id of "da," and the "Sector size" listed above the partition table. In this case it is partition 2 in which the start block is 5120 and the Sector size is 512. Multiply the two numbers to obtain the necessary offset: 5120 * 512 = 2621440.
Next, the initrd partition from the disk image file is mounted to a folder created in the working directory:
mkdir mnt
sudo mount -orw,loop,offset=$((5120*512)) diskimg.dd mnt/
The new XNAND2 nandctl binary is copied to the mounted folder structure
cp nandctl mnt/sbin/nandctl
sync
The disk image can be unmounted and renamed as needed:
sudo umount mnt
mv diskimg.dd diskimg-xnand2.dd
Note: | The new nandctl binary will only allow one instance of nandctl to run. |
If a script attempts to run nandctl while another copy of nandctl is already running, the binary will refuse to run.
This is different from previous versions that would allow for a race condition that could cause data corruption and filesystem damage. Some production processes use these old scripts included in the original TS-4800 image. Those scripts must also be modified to avoid calling nandctl while it is already running. In most cases those calls can be replaced with equivalents using the Linux dd utility instead, or in some cases the call can be dropped entirely. There are some rare cases where the existing nandctl instance must be killed in order to perform the lowest level activities, such as performing an xnandinit, but this drastic step is generally not necessary on systems already set up to use xnand2.
DIO
The TS-4800 has both CPU and FPGA DIO. In our schematics and documentation we will refer to the CPU DIO as GPIO#_#, and the FPGA DIO as DIO_##. Both are accessed through memory mapped registers. 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 memory table.
Full details on CPU pins can be found in the CPU manual, along with mode and mapping assignments specific to the CPU. Many of these pins support multiple functions which are described in Chapter 4: "External Signals and Pin Multiplexing". The GPIO functionality is described in chapter 35.
The common usage for these IO is abstracted through tsctl, or using the built in shell scripts:
# This next line is needed if you are in Debian
#source /initrd/ts.subr
# this should already be sourced in the initrd, but if you connect through
# telnet you will need to source it manually:
#source /ts.subr
# The CPU has gpio_ functions for its DIO:
# gpio_dir_get BANK(1..4) DIO_NUM(0..15)
gpio_dir_get 1 3
# gpio_dir_set BANK(1..4) DIO_NUM(0..15) DIR(0=input,1=output)
gpio_dir_set 1 3 0
# gpio_data_get BANK(1..4) DIO_NUM(0..15)
gpio_data_get 1 3
# to output GPIO1_3, set its direction to an output first:
gpio_dir_set 1 3 1
# gpio_data_set BANK(1..4) DIO_NUM(0..15) 0|1
gpio_data_set 1 3 1
# The FPGA DIO has equivalent functions:
# dio_dir_get BANK(1..4) DIO_NUM(0..15)
dio_dir_get 1 15
# dio_dir_set BANK(1..4) DIO_NUM(0..15) DIR(0=input,1=output)
dio_dir_set 1 15 0
# dio_data_get BANK(1..4) DIO_NUM(0..15)
dio_data_get 1 15
# dio_data_set BANK(1..4) DIO_NUM(0..15) 0|1
dio_data_set 1 15 1
This table describes the FPGA DIO banks which match up with our schematic and the rest of our documentation:
DIO Number | Bank | Bank DIO number |
---|---|---|
0:15 | 1 | 0:15 |
16:31 | 2 | 0:15 |
32:47 | 3 | 0:15 |
48:55 | 4 | 0:7 |
DIO Number | Connector Location | Alternate Function |
---|---|---|
0 | CN1_93 | Offboard IRQ 275 |
1 | CN1_91 | Offboard IRQ 276 |
2 | CN1_89 | Offboard IRQ 277 |
3 | CN1_87 | |
4 | CN1_85 | |
5 | CN1_83 | |
6 | CN1_81 | |
7 | CN1_79 | |
8 | CN1_77 | |
9 | CN1_73 | |
10 | CN1_71 | |
11 | CN1_69 | |
12 | CN1_67 | |
13 | CN1_65 | |
14 | CN1_63 | |
17 | CN2_70 | |
18 | CN2_64 | |
19 | CN2_62 | |
20 | CN2_54 | 24MHz clock (default) |
21 | CN1_94 | MUX_AD_00 |
22 | CN1_92 | MUX_AD_01 |
23 | CN1_90 | MUX_AD_02 |
24 | CN1_88 | MUX_AD_03 |
25 | CN1_86 | MUX_AD_04 |
26 | CN1_84 | MUX_AD_05 |
27 | CN1_82 | MUX_AD_06 |
28 | CN1_80 | MUX_AD_07 |
29 | CN1_78 | MUX_AD_08 |
30 | CN1_76 | MUX_AD_09 |
31 | CN1_74 | MUX_AD_10 |
32 | CN1_72 | MUX_AD_11 |
33 | CN1_70 | MUX_AD_12 |
34 | CN1_68 | MUX_AD_13 |
35 | CN1_66 | MUX_AD_14 |
36 | CN1_64 | MUX_AD_15 |
37 | CN1_96 | BUS_ALE# |
38 | CN1_98 | BUS_DIR |
39 | CN1_100 | BUS_CS# |
40 | CN1_99 | BUS_BHE# |
41 | CN1_97 | BUS_WAIT# |
42 | CN2_69 | SPI_MISO |
43 | CN2_71 | SPI_CLK |
44 | CN2_65 | SPI_CS# |
45 | CN2_67 | SPI_MOSI |
46 | CN2_99 | CAN_RXD |
47 | CN2_97 | CAN_TXD |
48 | CN2_80 | XUART0_RXD |
49 | CN2_78 | XUART0_TXD |
50 | CN2_92 | XUART1_RXD |
51 | CN2_90 | XUART1_TXD |
52 | CN2_96 | XUART2_RXD |
53 | CN2_94 | XUART2_TXD |
54 | CN2_100 | XUART3_RXD |
55 | CN2_98 | XUART3_TXD |
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.
Note: | Ethernet Magnetics should be placed as close to CN2 as possible on the base board. |
I2S Audio
These pins can be connected to an I2S CODEC for an audio output channel. Our default kernel contains a configuration for alsa support using an sgtl5000. See the provided Linux kernel for more information on other supported audio codecs.
MUXBUS
All TS-SOCKET System-on-Modules have an external bus called the MUXBUS. The bus consists of 16 muxed address/data lines, ALE#, CS#, DIR, BHE#, and WAIT#. The MUXBUS provides a way for TS-SOCKET base board designers to include static memory devices, bridges to other industry standard buses such as PC/104, or an FPGA that implements custom features. Verilog modules ts8100.v and muxbusbridge.v are provided as examples of how to interface with the MUXBUS.
MUXBUS slaves can be 8 bit or 16 bit devices. Each SoM has an 8 bit MUXBUS address space which must be accessed with 8 bit reads and writes, and a 16 bit MUXBUS address space which must be accessed with 16 bit reads and writes. Software that works with the MUXBUS must know whether it is talking to an 8 bit or 16 bit slave device and act accordingly.
The bus cycle speed depends on the FPGA clock speed, which varies from one SoM to another. Thus, the MUXBUS behavior is specified in clock cycles. The bus cycle works as follows:
- ALE# is asserted, and the address is driven on the bus lines. This condition is held for TP_ALE + 1 clock cycles.
- ALE# is de-asserted while the address remains valid for TH_ADR + 1 clock cycles.
- Data is driven on the bus lines (for a write) or the bus lines go high-Z (for a read) for TSU_DAT + 1 clock cycles.
- CS# is asserted for TP_CS + 1 clock cycles.
- CS# is de-asserted and data remains valid for TH_DAT + 1 clock cycles.
BHE# and DIR remain valid throughout the whole bus cycle. WAIT# is an input. The external device can assert the WAIT signal during the CS# pulse to extend it. The bus can work in 8 bit or 16 bit modes. In 8 bit mode, mux lines 8-15 are not used for data and BHE# is ignored. In 16 bit mode, byte reads and writes are still supported using BHE# and A0.
Each module will have a 16 bit external bus configuration register in its #Syscon.
Bus Config Register Bits | Usage |
---|---|
bit 0 | Bus enable (otherwise, pins are GPIO or reserved) |
bits 2:1 | TP_ALE |
bits 4:3 | TH_ADR |
bits 6:5 | TSU_DAT |
bits 12:7 | TP_CS [1] |
bits 15:13 | TH_DAT |
- ↑ A TP_CS of 0x3f is not supported -- use a value from 0 to 62 (that's 0x00 to 0x3e).
IMPLEMENTATION TIMING NOTES:
On a MUXBUS write, all timing values are controlled by the bus config register. The slave device is permitted to latch data on either the leading edge or the trailing edge of the CS# pulse, or any time in between.
One a MUXBUS read, the MUXBUS latches data on the trailing edge of the CS# pulse. The slave device should begin driving the data bus in response to CS# assertion. Users should program TP_CS so that TSU_RD is at least 10ns plus any delays between the two boards. A conservative TP_CS setting is recommended, because an extra clock cycle here will not have a significant effect on net MUXBUS bandwidth.
The slave device must stop driving the data bus in response to CS# de-assertion. TH_RD must be at most 30ns. The TS-4800 uses 13.33ns clock cycle.
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.
Offset | Bits | Access | Description | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x0 | 15:0 | Read/Write | Address bits 26-11 | ||||||||||
0x2 | 15:14 | Read/Write |
| ||||||||||
13:12 | Read/Write |
| |||||||||||
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] |
- ↑ Write 1 16 bit data register to write 2x 8bit cycles.
- ↑ Write 2 16 bit values to perform 1 32 bit cycle.
- ↑ 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.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;
close(mem);
}
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)
{
mwinen();
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.
Interrupts
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
usleep(10000);
}
return 0;
}
IRQ # | Name | Socket Location |
---|---|---|
50 | Combined Interrupt GPIO1_0 - GPIO1_15 | CN2_56, CN2_58, CN2_60, CN2_72, CN1_57, CN1_4 |
56 | Combined Interrupt GPIO4_0 - GPIO4_15 | CN2_13, CN2_14 |
275 | IRQ5/DIO_00 | CN1-93 |
276 | IRQ6/DIO_01 | CN1-91 |
277 | IRQ7/DIO_02 | CN1-89 |
UARTs
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
External Interfaces
TS-Socket
The TS-SOCKET System-on-Modules (SoMs) all use two high density 100 pin connectors for power and all I/O. These follow a common pinout for various external interfaces so new modules can be dropped in to lower power consumption or use a more powerful processor. The male connector is on the baseboard, and the female connector is on the SoM. You can find the datasheet for the baseboard's male connector here. This can be ordered from the TS-Socket SoM product page as CN-TSSOCKET-M-10 for a 10 pack, or CN-TSSOCKET-M-100 for 100 pieces, or from the vendor of your choice, the part is an FCI "61083-102402LF".
We have an Eaglecad library available for developing a custom baseboard here. We also provide the entire PCB design for the TS-8200 baseboard here which you can modify for your own design.
In our schematics and our table layout below, we refer to pin 1 from the male connector on the baseboard.
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:
- The Debian Handbook (online or book)
- Learning Debian GNU/Linux (book)
- Debian Administration (online)
For Linux programming in general:
- The Linux Documentation Project (online)
- The Linux Programming Interface (book)
- Linux System Programming (book)
- Linux in a Nutshell (book)
Errata
Synopsis | TS-4800 MUXBUS overflow |
Severity | Minor |
Class | FPGA Implementation Characteristic |
Affected | All TS-4800 applications making use of custom MUXBUS peripherals |
Status | Workarounds available |
The TS-4800 MUXBUS implementation does not account for peripherals with excessively slow read speeds. If the peripheral fails to empty the buffer in a timely manner, data will be lost during the resulting buffer overflow. There is no buffer-overflow indicator for the MUXBUS write functionality.
Workarounds:
Read from the MUXBUS at least once for every 16 write cycles. A read should force a flush of the buffer (blocking further CPU activity until the buffer has emptied).
Ensure peripheral design complies with documented muxbus timing such that the write buffer does not overflow.
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.
Trademarks
Arm and Cortex are registered trademarks of Arm Limited (or its subsidiaries) in the US and/or elsewhere.