TS-4300
This product is under engineering sampling, and is still under development.
This product supports applying firmware updates to the FPGA and onboard microcontroller (Wizard) so many features will be provided as updates in the field as they are completed.
Feature | Status | Notes |
---|---|---|
Ethernet | Working | Tested iperf on both ports to validate 1Gb/s. Tested nfs booting, no issues seen with Ethernet traffic. Some features like PTP are currently not tested. |
eMMC | Working | Preliminarily Tested. The bootloader is loaded from mmc0 boot partition 0. Tested booting to Linux and reading/writing. |
microSD | Working | Preliminarily Tested |
i.MX93 NPU | Working | Tested with 6.6.52 kernel. See NPU documentation section for script to install software support. |
USB Host Ports | Working | Tested USB drives enumerate and read/write data. |
RTC | Working | Verified this RTC from the wizard holds and retains time. |
FPGA PWM controller | Working | Preliminarily Tested, final PWM count will change once remaining fpga core are implemented |
Bluetooth | Working | Preliminarily Tested, verified bluetoothctl scanning & discoverable |
DIO | Working | Tested CPU IO and FPGA controllers to be working, and support interrupts on rising/falling/both edges. |
WIFI | Testing | Preliminarily working, but feature improvements for next hardware revision. |
Baseboard Overlays | TODO | Each baseboard will load a device tree overlay to the "imx93-ts4300.dtb", using U-Boot's "Expansion Board" concept.
https://docs.u-boot.org/en/v2025.04/usage/cmd/extension.html Our u-boot will be updated to load: imx93-ts4300-<baseboardid>.dtbo. Examples will be provided for the TS-8551 and some common customizations. |
Serial Ports | TODO | UARTs are working, not yet tested for carrier board transceivers |
CAN | TODO | |
SPI | TODO | |
FPGA XBAR | TODO | This will be provided as an FPGA update, and Linux driver update. This will allow switching around various FPGA interfaces to differenet FPGA pins. |
EXT_RESET# | TODO | This pin will be supported as both a GPIO, and by default will be an external reset that will instantly reset the board when held low. This will be implemented with the FPGA XBAR. |
TS-SILO Supercap Backup | TODO | The TS-SILO will provide backup power after power has been lost, which provides time to properly shutdown and unmount drives after power has been lost. This has been preliminarily tested in the hardware and will be provided as a future Wizard update, and Linux driver update. |
i.MX93 low power sleep | TODO | This is the low power mode in the cpu where we can suspend to ram. This requires further documentation / testing |
Wizard low power sleep | TODO | This feature will allow powering off the i.MX93, FPGA, and most ICs, but allow waking on RTC alarm, push button, and planned on a future PCB revision is movement from the accelerometer/gyro. |
i.MX93 Low drive mode | TODO | This lower performance / operating mode that allows lower runtime power. This requires further changes in the FPGA/linux to implement. |
i.MX93 M33 | TODO | This is the ARM Cortex-M33 microcontroller built into the i.MX93, allowing users to run some code in an RTOS like Zephyr or FreeRTOS. This is expected to be a u-boot configuration and linux change only |
Image Replicator | TODO | This is the USB tool to rewrite the image on production units. This product support will be added in the future. |
Open Source FPGA | TODO | The FPGA is planned to be open sourced, and will be after some additional features are stabilized. |
![]() | |
Product Page | |
Documentation | |
---|---|
Schematic | |
FTP Path | |
Processor | |
NXP i.MX9352 | |
1.7 GHz Arm® Cortex®-A55/M33 | |
i.MX93 Product Page | |
CPU Documentation |
Overview
Getting Started
U-Boot
U-Boot is a bootloader and comes preinstalled on this board. The U-Boot bootloader is loaded in the eMMC hardware boot partitions in /dev/mmcblk0boot0. U-Boot sets up the hardware and then loads the OS from the available storage devices. U-Boot allows booting images from the microSD, eMMC, NFS, or USB. Most users will not need to customize u-boot further, and can proceed to the #Debian, #Ubuntu, #Yocto, or #Buildroot sections for information on application development.
U-Boot Standard Boot
This platform uses u-boot's "Standard Boot" as the method to search available storage media for a bootable operating system.
By default the board will attempt booting to these devices:
boot_target | Description |
---|---|
usb | U-boot will probe for any available USB Storage media |
mmc1 | microSD |
mmc0 | Onboard eMMC flash |
pxe | Network boot |
The order, and the enabled boot targets can be customized by changing the variable:
# Boot to eMMC only:
setenv boot_targets mmc0
# Boot to USB, then eMMC:
setenv boot_targets usb mmc0
# Default targets
setenv boot_targets usb mmc1 mmc0 pxe
While searching each media, u-boot searches for valid boot methods on each device.
bootmeths | Description |
---|---|
script | Legacy distro-boot scripts. Looks for /boot/boot.scr, /boot/boot.scr.uimg, and executes those |
extlinux | Looks for extlinux/extlinux.conf. See the syslinux project for more details. |
efi | Looks for an EFI boot partition, and executes the EFI payload such as grub, or the Linux kernel. |
pxe | For PXE boot only, checks for valid DHCP describing boot. |
The order, and the enabled boot methods can be customized by changing the variable:
# Only boot extlinux:
setenv bootmeths extlinux
# Try script, then EFI
setenv bootmeths script efi
# Default bootmeths
setenv bootmeths script extlinux efi pxe
On our factory preprogrammed images the typical boot skips USB, mmc1, and boots to a script on eMMC.
U-Boot Baseboard/Extension Support
In the selected boot method u-boot loads a device tree which describes the hardware the kernel runs on. The imx93-ts4300.dtb is always loaded to describe the TS-4300 hardware. Every baseboard is supported using a device tree overlay.
This extends the TS-4300 device tree with the features needed to support the carrier board. In u-boot, this is loaded using u-boot's extension support.
All of our off the shelf baseboards contain a hard-wired 8-input multiplexer to indicate the baseboard model. This is not required to implement in custom baseboards, but it can be useful to identify the board model in software. The u-boot extension will scan the baseboard id, eg, the TS-8551 which is hard wired as 0x16.

- S0 is connected to CN2_006
- S1 is connected to CN2_008
- S2 is connected to CN1_098
- BD_ID_DATA is connected to CN1_083
The 6 least significant input pins (Y0 - Y5) are used to define the baseboard model. The upper two inputs (Y6 and Y7) define board revision.
The baseboard id is specified in hex, so the TS-8551 searches for:
imx93-ts4300-16.dtbo
If it exists, it is applied automatically with "extension apply all" in our standard boot scripts. Custom carrier boards should use the ID "0x2a", which will never be used by our off the shelf boards.
U-Boot Environment
Booting From NFS
U-Boot Development
Debian
Debian 12 - Bookworm
Debian 12 - Getting Started
This Debian release is available in 3 flavors with various packages.
Image | Estimated Size | Description |
---|---|---|
tsimx9-debian-12-bookworm-x11-latest.tar.xz | 1233 MiB |
|
tsimx9-debian-12-bookworm-headless-latest.tar.xz | 961 MiB |
|
tsimx9-debian-12-bookworm-minimal-latest.tar.xz | 379 MiB |
|
The default login is root with no password.
To write this to an SD card, first partition the SD card to have one large ext4 partition. Once it is formatted, extract this tar with:
# Assuming your SD card is /dev/sdc with one partition
mkfs.ext4 /dev/sdc1
mkdir /mnt/sd/
sudo mount /dev/sdc1 /mnt/sd/
sudo tar --numeric-owner -xJf tsimx9-debian-12-bookworm-x11-latest.tar.xz -C /mnt/sd
sudo umount /mnt/sd
sync
To rewrite the eMMC, boot to the SD card. You cannot rewrite the emmc while it is mounted elsewhere, or used to currently boot the system. Once booted to the SD, run:
mkfs.ext3 /dev/mmcblk2p1
mkdir /mnt/emmc
mount /dev/mmcblk2p1 /mnt/emmc
wget -qO- https://files.embeddedts.com/ts-arm-sbc/ts-9370-linux/distributions/debian/ tsimx9-debian-12-bookworm-x11-latest.tar.xz | tar --numeric-owner -xJ -C /mnt/emmc/
umount /mnt/emmc
sync
Debian 12 - Networking
The network in Debian is configured with /etc/network/interfaces. For complete documentation, see Debian's documentation here
Some common examples are shown below. On this release network interfaces follow the predictible network interface names. Run ip addr show
to get a list of the network interfaces.
Most commonly:
- end0 - Ethernet device 0 (CPU Ethernet)
- enp1s0 - Ethernet PCIe port 1 slot 0 ethernet
- usb<mac> - USB ethernet
- wlan0 - WIFI
DHCP on end0. Edit the file /etc/network/interfaces and add:
auto end0 allow-hotplug end0 iface end0 inet dhcp
Static IP on end0. Edit the file /etc/network/interfaces and add:
auto end0 iface end0 inet static address 192.0.2.7/24 gateway 192.0.2.254
These will take effect on the next boot, or by restarting the networking service:
service networking restart
Debian 12 - WIFI Client
Wireless interfaces are also managed with configuration files in "/etc/network/interfaces.d/". For example, to connect as a client to a WPA network with DHCP. Note some or all of this software may already be installed on the target SBC.
Install wpa_supplicant:
apt-get update && apt-get install wpasupplicant -y
Run:
wpa_passphrase youressid yourpassword
This command will output information similar to:
network={ ssid="youressid" #psk="yourpassword" psk=151790fab3bf3a1751a269618491b54984e192aa19319fc667397d45ec8dee5b }
Use the hashed PSK in the specific network interfaces file for added security. Create the file:
/etc/network/interfaces.d/wlan0
allow-hotplug wlan0
iface wlan0 inet dhcp
wpa-ssid youressid
wpa-psk 151790fab3bf3a1751a269618491b54984e192aa19319fc667397d45ec8dee5b
To have this take effect immediately:
service networking restart
For more information on configuring Wi-Fi, see Debian's guide here.
Debian 12 - WIFI Access Point
First, hostapd needs to be installed in order to manage the access point on the device:
apt-get update && apt-get install hostapd -y
Note: | The install process will start an unconfigured hostapd process. This process must be killed and restarted before a new hostapd.conf will take effect. |
Edit /etc/hostapd/hostapd.conf to include the following lines:
interface=wlan0 driver=nl80211 ssid=YourAPName channel=1
Note: | Refer to the kernel's hostapd documentation for more wireless configuration options. |
To start the access point launch hostapd:
hostapd /etc/hostapd/hostapd.conf &
This will start up an access point that can be detected by WIFI clients. A DHCP server will likely be desired to assign IP addresses. Refer to Debian's documentation for more details on DHCP configuration.
Debian 12 - Installing New Software
Debian provides the apt-get system which allows management of pre-built applications. The apt tools require a network connection to the internet in order to automatically download and install new software. The update command will download a list of the current versions of pre-built packages.
apt-get update
A common example is installing Java runtime support for a system. Find the package name first with search, and then install it.
root@tsa38x:~# apt-cache search openjdk default-jdk - Standard Java or Java compatible Development Kit default-jdk-doc - Standard Java or Java compatible Development Kit (documentation) default-jdk-headless - Standard Java or Java compatible Development Kit (headless) default-jre - Standard Java or Java compatible Runtime default-jre-headless - Standard Java or Java compatible Runtime (headless) jtreg - Regression Test Harness for the OpenJDK platform libreoffice - office productivity suite (metapackage) openjdk-11-dbg - Java runtime based on OpenJDK (debugging symbols) openjdk-11-demo - Java runtime based on OpenJDK (demos and examples) openjdk-11-doc - OpenJDK Development Kit (JDK) documentation openjdk-11-jdk - OpenJDK Development Kit (JDK) openjdk-11-jdk-headless - OpenJDK Development Kit (JDK) (headless) openjdk-11-jre - OpenJDK Java runtime, using Hotspot JIT openjdk-11-jre-headless - OpenJDK Java runtime, using Hotspot JIT (headless) openjdk-11-jre-zero - Alternative JVM for OpenJDK, using Zero openjdk-11-source - OpenJDK Development Kit (JDK) source files uwsgi-app-integration-plugins - plugins for integration of uWSGI and application uwsgi-plugin-jvm-openjdk-11 - Java plugin for uWSGI (OpenJDK 11) uwsgi-plugin-jwsgi-openjdk-11 - JWSGI plugin for uWSGI (OpenJDK 11) uwsgi-plugin-ring-openjdk-11 - Closure/Ring plugin for uWSGI (OpenJDK 11) uwsgi-plugin-servlet-openjdk-11 - JWSGI plugin for uWSGI (OpenJDK 11) java-package - Utility for creating Java Debian packages
In this case, the wanted package will likely be the "openjdk-11-jre" package. Names of packages can be found on Debian's wiki pages or the packages site.
With the package name apt-get install can be used to install the prebuilt packages.
apt-get install openjdk-11-jre
# More than one package can be installed at a time.
apt-get install openjdk-11-jre nano vim mplayer
For more information on using apt-get refer to Debian's documentation here.
Debian 12 - Setting up SSH
Openssh is installed in our default Debian image, but by default openssh does not permit root logins, and requires a password to be set. Additionally, a host key is required if one hasn't already been created on the target board. To allow remote root login:
sed --in-place 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
systemctl restart ssh.service
passwd root # Set any password
If you ssh to this system it will now support ssh as root.
Debian 12 - Starting Automatically
A systemd service can be created to start up headless applications. Create a file in /etc/systemd/system/yourapp.service
[Unit]
Description=Run an application on startup
[Service]
Type=simple
ExecStart=/usr/local/bin/your_app_or_script
[Install]
WantedBy=multi-user.target
If networking is a dependency add "After=network.target" in the Unit section. Once you have this file in place add it to startup with:
# Start the app on startup, but will not start it now
systemctl enable yourapp.service
# Start the app now, but doesn't change auto startup
systemctl start yourapp.service
Note: | See the systemd documentation for in depth documentation on services. |
Debian 12 - Cross Compiling
Debian provides cross compilers in their matching distribution. For example, if you are running Debian 12 on your workstation or VM:
sudo dpkg --add-architecture arm64
sudo apt-get update
# Cross compiler
sudo apt-get install gcc-aarch64-linux-gnu
# Install any needed libraries/headers from Debian 12 for arm64:
sudo apt-get install libc6-dev:arm64 libgpiod-dev:arm64
A hello world can be built with:
aarch64-linux-gnu-gcc hello.c -o hello
Tools like docker/cqfd can make this simpler to run Debian 12 in a container just for the build. See our cqfd hello world project that demonstrates a simpler way to run these from most Linux systems.
Debian 12 - Compile the aarch64 Kernel
For building kernels targeting the Debian images we recommend using cqfd. This allows building a compatible kernel using identical tools from any build host.
To build a kernel for the embeddedTS i.MX93 based platforms:
- Make sure docker is installed, and can run hello world:
- docker run hello-world
- Make sure cqfd is installed:
# Install cqfd-linux-lts builds scripts
git clone https://github.com/embeddedTS/cqfd-linux-lts
cd cqfd-linux-lts
# Clone the Linux kernel
git clone https://github.com/embeddedTS/prototype-linux-imx93.git -b lf-6.6.y-ts linux
# Build the kernel:
./build_tsimx93_tar.sh
This will set up the container including Debian's cross compilers, and any dependencies needed to build the kernel. This runs once on initial setup, and the only when the Dockerfile is modified. Next, it builds the defconfig, compiles the kernel, and assembles a tar of the build objects for that image. After it completes, it will print out the path to the tar file.
At this point, the tarball can be unpacked to a bootable media for the device. This can be done from a booted device, or by mounting removable media from a host Linux workstation. For example, if the root folder of the target filesystem to be updated is mounted to /mnt/, the following can be used to unpack the above tarball:
# Ensure the target filesystem is mounted to /mnt first!
# Extract kernel tarball to target filesystem,
tar xhf linux-*.tar.gz -C /mnt
Note: | The tar h argument to tar is necessary on recent distributions that merge /usr/. Not using it will render the system unbootable. |
Ubuntu
Buildroot
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. For example, to restore a device to its factory settings or apply a customized image/filesytem for application deployment. Additionally, specific units may be used for development and that unit's disk images need to be replicated to other units to be deployed in the field.
We offer a number of different ways to accomplish both capturing images to be written to other units, and the actual writing process itself. See the sections below for details on our USB Image Replicator tool to capture and/or write images, as well as details on manual processes to capture and write images on each of this device's media.
Image Replicator
This platform supports our Image Replicator tool. The Image Replicator tool is intended for use by developers as a means to write bootable images or filesystems on to a device's media (SD / eMMC / SATA / etc.) as part of their production or preparation process. In addition to writing media, the Image Replicator tool is capable of capturing images from a device's media and preparing them to be written to other devices.
The Image Replicator tool is a USB disk image that can be booted on a target device to capture or write its media directly without the need for a host workstation. The USB disk image is based on Buildroot and contains a set of scripts which handle the capture and write process. The process and its scripts are flexible and can be used as-is or adapted in to larger production processes to format and load data on to devices. The single USB drive can be used to capture images from a device, and then can be inserted in to other devices to write those same images on to other devices. The capture process is not necessary if it is not needed. Images for the target device can be copied to the USB drive, booted on compatible units, and have the target images written to that unit's media.
Image Capture Process
The image capture process performs the following steps. For more detailed information, see the Image Capture section below.
- If no valid images exist on the disk, image capture starts.
- For each valid media present on the unit, a bit for bit copy of the source is made.
- This image is mounted, sanitized (to remove unneeded files and allow safe copying of the image to other units), and saved as either a disk image or a tarball depending on the partition layout of the source disk.
- All images and tarballs are compressed, with both the output files having their MD5 hash saved as well as all of the files contained in the root partition having their MD5 hashes saved to a file for later verification.
The captured images and tarballs are named such that the USB Image Replicator disk can be immediately used to boot another unit and have it perform the Image Write process to write that unit's media with the captured images.
Note: | When using this process, the USB drive used for the Image Replicator must be sized large enough to handle multiple compressed images as well as the uncompressed copy of the media image actively being worked with. If the image capture process runs out of space, the process will indicate a failure. |
Image Write Process
The image write process performs the following steps. For more details information see the Image Write section below.
- For each valid media present on the unit, find the first valid source image file for it.
- If a source image exists for a media that is not present on the unit, then the process indicates a failure.
- If the source image is a tarball, format the target disk with an appropriate filesystem, and unpack it to the target disk, verifying all files against the MD5 hash file list after they are written.
- If the source image is a disk image, write that to the target disk. If an MD5 file for the disk image exists, read back the written disk and compare it to the hash.
Creating a USB Image Replicator Disk
Creating USB image replicator i.MX93
Running the Image Replicator Tool
Once a USB drive is formatted with the Image Replicator tool (see Creating a USB Image Replicator Disk for the correct files and process), boot to this USB drive (note that the Image Replicator already sets up the correct U-Boot boot scripts to boot to the USB drive, see the aforementioned section for details on how to make U-Boot call the scripts on the USB drive). This will start either image capture if no disk images/tarballs are present on the USB drive, or image write if there are disk images/tarballs present on the USB drive.
Image Replicator Runtime Options
Some of the runtime operations of the Image Replicator can be specified. These are handled by creating empty files in the root folder of the bootable USB drive. When booted, these files are analyzed and actions taken based on them.
Option | Description |
---|---|
|
When capturing, skip media matching this name. See the respective platform manual for information on which names correspond to which physical media. Note that the names are generic and match what the media is captured as, regardless of actual device node. The names are uniform between capture and write for a given system. |
IR_NO_COMPRESS
|
When capturing, do not compress the data. On slower systems, slower disks, or systems with a large amount of data to capture, the final compression can take a significant amount of time. This option will allow a capture, but will not attempt to compress it. |
IR_SHELL_ONLY
|
When booting, skip doing any of the image replication process (Capture or Write) and instead drop to a login prompt. Useful for debugging. Note that, to prevent any confusion the system will indicate a non-critical failure when skipping any of the Image Replication process. |
Image Replicator LED Status
The green and red LEDs of the platform are used to indicate status.
Any LED patterns not matching the table below indicate different operational states of the platform itself, e.g. executing the bootloader, the kernel is running but Image Replicator has not yet started, etc.
Green | Red | State | Description |
---|---|---|---|
Short Strobe | Solid | Running | The Image Replicator process is running. |
0.5 Hz Blink | Off | Succeeded | All operations being performed by the Image Replicator have completed successfully. |
Off | 0.5 Hz Blink | Non-critical Failure | One or more operations being performed by the Image Replicator have failed to complete. View logs in /tmp/logs/ as well as the failure reason in /tmp/failed
|
Off | 4 Hz Blink | Critical Failure | An operation has failed in a way that could leave the device inoperable if power were to be removed. View logs in /tmp/logs as well as the failure reason in /tmp/failed .
|
Image Capture
If no valid images to write exist on the booted USB Image Replicator drive, the image capture process starts automatically.
Note that while in progress, the USB Image Replicator drive is mounted read-write. It is not advised to remove power or disconnect the USB Image Replicator drive until the whole process has completed.
To help diagnose failures, files in /tmp/logs/
contain output from each capture process.
For each media present on the unit (SD / eMMC / SATA / etc.), the image capture process will do the following:
- Copy the entire media image to an appropriately named file on the USB Image Replicator drive, e.g.
sdimage.dd
. No data is written to the source media and it is never mounted. The source disk can follow the stock partition layout, or implement a customized one. - Perform an fsck on the Linux rootfs partition in the image file. Note that, if deviating from the standard partition layout, it may be necessary to modify the scripts handling the capture process.
- Mount the Linux rootfs partition from the image file and sanitize the filesystem. The sanitization process removes temporary files (e.g.
/log/
files), unique files (e.g. ssh private key files, machine ID files), adds a file to indicate that it is a custom image with the date as its contents, etc. The full list of operations can be found in this script. It may be necessary to modify this file for unique situations. - If the media's partition layout uses only a single partition, the filesystem is packed in to a tarball on the USB Image Replicator drive which is appropriately named and compressed, e.g.
sdimage.tar.xz
. The image file is then unmounted and removed from the USB Image Replicator drive. - If the media's partition layout uses multiple partitions, the image file is then unmounted, an md5sum of the image file taken, it is compressed and appropriately named on the USB Image Replicator drive, e.g.
emmcimage.dd.xz
, and then an md5sum of the compressed image is taken.
Note that when using this process, the USB Image Replicator drive that is used must be sized large enough to handle multiple compressed images as well as the uncompressed copy of the media image actively being worked with. If the image capture process runs out of space, the process will indicate a failure via the LEDs.
The images files captured are saved to the root folder of the USB Image Replicator drive. Upon completion, it is safe to remove power or unplug the USB drive.
For more details on the image capture process, see this script.
Image Write
This process is used to write existing images to media on a target unit. If appropriately named disk images or tarballs (see table below) are present in the root folder of the USB Image Replicator drive when booted, then the startup scripts will start the image writing process. The latest disk images we provide for our platforms can be downloaded from our FTP site, see the backup and restore section for links to these files.
Note that the USB Image Replicator drive remains read-only through the entire process but target devices may be mounted or actively written. It is not advised to remove power or disconnect the USB Image Replicator drive until the whole process has completed.
To help diagnose failures, files in /tmp/logs/
contain output from each writing process.
The Image Replicator script expects disk images or tarballs to have specific names to match the target media. The script will attempt to match tarball and then disk image names (in the order they are listed in the table below) for each target media, using the first file that is found to write to the target media. Note that symlinks can be used on the USB Image Replicator disk if formatted with a filesystem that supports symlinks. This can be used, for example, to write the same tarball to both SD and eMMC from only a single copy of the source tarball.
Upon completion, it is safe to remove power or unplug the USB drive.
For more details on the image write process, see this script.
The following table is the list of valid file names and how they are processed:
Target media | Accepted filenames | Description |
---|
SD Card |
|
Tar of the filesystem. This will repartition the SD card to a single partition and extract this tarball to the filesystem. If present, a file named /md5sums.txt in the tarball will have its contents checked against the whole filesystem after the tarball is extracted. This md5sums.txt file is optional and can be omitted, but it must not be blank if present. This file is present in our official images and is created during image capture with the Image Replicator tool.
|
---|---|---|
|
Disk image of the media. This will be written to the SD card block device directly. If present on the USB Image Replicator drive, a file named /sdimage.dd.md5 will be used to verify the data written to the SD card against this checksum. This file is provided with our official images and is created during image capture with the Image Replicator tool.
|
eMMC |
|
Tar of the filesystem. This will repartition the eMMC to a single partition and extract this tarball to the filesystem. If present, a file named /md5sums.txt in the tarball will have its contents checked against the whole filesystem after the tarball is extracted. This md5sums.txt file is optional and can be omitted, but it must not be blank if present. This file is present in our official images and is created during image capture with the Image Replicator tool.
|
---|---|---|
|
Disk image of the media. This will be written to the eMMC block device directly. If present on the USB Image Replicator drive, a file named /emmcimage.dd.md5 will be used to verify the data written to the SD card against this checksum. This file is provided with our official images and is created during image capture with the Image Replicator tool.
|
U-Boot |
|
U-Boot binary blob. This will be written to the bootloader area of eMMC. Note that both files are required for U-Boot, if either file is missing then the Image Replicator tool will not write either of them. If the file /SPL.md5 is present on the USB drive, this will be used to verify the data written to disk.
|
---|
Building the Image Replicator from Source
The Image Replicator tool uses Buildroot to create the bootable USB disk image and tarball. See the project repository on github for information on compatibility and instructions on building: https://github.com/embeddedTS/buildroot-ts
microSD Card
Note: | Our Image Replicator tool can be used to automate this process. |
TS-4300 MicroSD Backup/restore
Booted from SD
Note: | Our Image Replicator tool can be used to automate this process. |
Features
Battery-backed RTC
Bluetooth
CAN
The TS-4300 provides two CAN controllers, one on CN2_97/CN2_99, and the second on CN1_69/CN1_71. These TTL signals require a transceiver such as the NXP TJA1462A.
The i.MX93 includes 2 CAN controllers. These support CAN 2.0B, and CAN FD. Both of these include drivers which support the SocketCAN interface. Before proceeding with the examples, see the Kernel's CAN documentation here.
## First, set the baud rate and bring up the device:
ip link set can0 type can bitrate 250000
ip link set can0 up
## Dump data & errors:
candump can0 &
## Send the packet with:
#can_id = 0x7df
#data 0 = 0x3
#data 1 = 0x1
#data 2 = 0x0c
cansend can0 7DF#03010C
The above example packet is designed to work with the Ozen Elektronik myOByDic 1610 ECU simulator to read the RPM speed. In this case, the ECU simulator would return data from candump with:
<0x7e8> [8] 04 41 0c 60 40 00 00 00 <0x7e9> [8] 04 41 0c 60 40 00 00 00
In the output above, columns 6 and 7 are the current RPM value. This shows a simple way to prove out the communication before moving to another language.
The following example sends the same packet and parses the same response in C:
#include <stdio.h>
#include <pthread.h>
#include <net/if.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <assert.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int main(void)
{
int s;
int nbytes;
struct sockaddr_can addr;
struct can_frame frame;
struct ifreq ifr;
struct iovec iov;
struct msghdr msg;
char ctrlmsg[CMSG_SPACE(sizeof(struct timeval)) + CMSG_SPACE(sizeof(__u32))];
char *ifname = "can0";
if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("Error while opening socket");
return -1;
}
strcpy(ifr.ifr_name, ifname);
ioctl(s, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("socket");
return -2;
}
/* For the ozen myOByDic 1610 this requests the RPM guage */
frame.can_id = 0x7df;
frame.can_dlc = 3;
frame.data[0] = 3;
frame.data[1] = 1;
frame.data[2] = 0x0c;
nbytes = write(s, &frame, sizeof(struct can_frame));
if(nbytes < 0) {
perror("write");
return -3;
}
iov.iov_base = &frame;
msg.msg_name = &addr;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &ctrlmsg;
iov.iov_len = sizeof(frame);
msg.msg_namelen = sizeof(struct sockaddr_can);
msg.msg_controllen = sizeof(ctrlmsg);
msg.msg_flags = 0;
do {
nbytes = recvmsg(s, &msg, 0);
if (nbytes < 0) {
perror("read");
return -4;
}
if (nbytes < (int)sizeof(struct can_frame)) {
fprintf(stderr, "read: incomplete CAN frame\n");
}
} while(nbytes == 0);
if(frame.data[0] == 0x4)
printf("RPM at %d of 255\n", frame.data[3]);
return 0;
}
See the Kernel's CAN documentation here. Other languages have bindings to access CAN such as Python, Java using JNI.
In production use of CAN we also recommend setting a restart-ms for each active CAN port.
ip link set can0 type can restart-ms 100
This allows the CAN bus to automatically recover in the event of a bus-off condition.
CPU
The CPU is a i.MX9352 CPU which is a dual core Arm® Cortex®-A55 at 1.7GHz. This is a 64-bit Arm® v8.2-A architecture core.
- 32 KB L1 Instruction Cache
- 32 KB L1 Data Cache
- 64 KB per-core L2 cache
- Media Processing Engine (MPE) with Arm® NeonTM technology supporting the
Advanced Single Instruction Multiple Data architecture
- Floating Point Unit (FPU) with support of the Arm® VFPv4-D16 architecture
eMMC Interface
The onboard eMMC supports the eMMC5.1 specification, and SDR104 speeds with an 8-bit bus. The eMMC performance provides high speed access, typically with larger eMMC providing faster speeds:
- 32 GB eMMC
- 118 MB/s Write
- 311 MB/s Read
- 16 GB eMMC
- 107 MB/s Write
- 175 MB/s Read
The onboard eMMC supports fast sequential access of approximately 250 MiB/s read or 110MiB/s write.
The eMMC provides 4 devices:
Device | Description |
---|---|
/dev/mmcblk0 | User partition that includes GPT partitions and bootable OS |
/dev/mmcblk0boot0 | Bootloader partition that includes ATF/OP-TEE/DDR firmware/U-Boot, and u-boot environment |
/dev/mmcblk0boot1 | Unused bootloader partition |
/dev/mmcblk0rpmb | Replay protected memory block |
Besides including a bootable image, /dev/mmcblk0 does not include any preallocated data at various offset.
The mmcblk0boot0 partition does include data at some specific offsets. This device is not guaranteed to be the same size on all equivalent parts, but will provide at least 4MB.
Offset | Description |
---|---|
0x0-0x1FFFFF | Singleboot image including arm-trusted-firmware, LPDDR4 firmware, OP-TEE, imx93 Sentinal firmware, and U-Boot. |
0x200000-0x203FFF | U-boot environment |
0x204000-0x207FFF | U-boot environment (Redundant) |
0x240000-0x2BFFFF | Pending FPGA Update Image |
Ethernet Ports
FPGA
FPGA Bus
The TS-4300's FPGA is connected to the CPU over the FlexSPI bus. This provides 32-bit access to the FPGA, mapped at 0x2800_0000
.
FPGA Memory Map
Offset | Description |
---|---|
0x0000 | Model/Rev Info |
0x0020 | IRQ core |
0x0040 | FPGA GPIO block #0 |
0x0080 | FPGA GPIO block #1 |
0x00C0 | FPGA GPIO block #2 |
0x0100 | FPGA GPIO block #3 |
0x0400 | FPGA PWM 0 |
0x2000 | FPGA XBAR |
FPGA Info Core
The FPGA model/info core is used for testing / identification of the board. In general user should not need to implement access to this core without a custom FPGA, but the register interface is as follows:
# Read the MODEL register
memtool md 0x28000000+4
Address | Name | Bits | Field | RO/RW | Description |
---|---|---|---|---|---|
0x0 | MODEL | [31:16] | Reserved | RO | Reserved bits |
[15:0] | Model Number | RO | Model number of the core (e.g., 0x9370, 0x9390, 0x4300) | ||
0x4 | VERSION | [31] | Git Dirty Bit | RO | Indicates if the working tree is dirty |
[30:24] | Major Tag Version | RO | Major version number | ||
[23:16] | Minor Tag Version | RO | Minor version number | ||
[15:8] | Patch Tag Version | RO | Patch version number | ||
[7:0] | Commits Since Tag | RO | Number of commits since the tag | ||
0x8 | GIT_INFO | [31:0] | Git Short Hash | RO | 31-bit Git short hash |
0xC | BUILD_TIME | [31:0] | Build Time | RO | Build time encoded as epoch ≫ 1 |
0x10 | SCRATCH0 | [31:0] | Scratch Reg 0 | RW | General-purpose scratch register 0 |
0x14 | SCRATCH1 | [31:0] | Scratch Reg 1 | RW | General-purpose scratch register 1 |
0x18 | UNIQUE_IDENTIFIER | [31:0] | Unique Identifier | RW | Custom identifier for user-built FPGA images (0 for stock) |
FPGA GPIO Core
The GPIO core supports:
- set/clr regs for data and oe
- IRQ level/edge, edge select between rising/falling/both edges
- 32 IO per bank.
For Linux usage of the GPIO controller, see the GPIO section which shows how to use the GPIO/IRQs from userspace. While we recommend using the existing driver stack, the register documentation the 32-bit registers controlling each block are defined as follows:
Address | Name | Read/Write | Description |
---|---|---|---|
0x00 | oe | Read | Output enable state of GPIO pins. |
Write | Set bits to enable output for corresponding GPIOs. | ||
0x04 | oe_clr | Read | N/A |
Write | Clear bits to disable output for corresponding GPIOs. | ||
0x08 | output_data | Read | Current state of GPIO output data. |
output_data_set | Write | Set bits to drive high the corresponding GPIO outputs. | |
0x0C | input_data | Read | Current state of GPIO input data. |
output_data_clr | Write | Set bits to drive low the corresponding GPIO outputs. | |
0x10 | irq_pending | Read | Shows active and unmasked interrupts. 0 = Inactive/masked, 1 = Active and unmasked. |
Write | Acknowledge interrupt. 0 = No effect, 1 = Ack interrupt. | ||
0x14 | irq_mask | Read | Shows IRQ Mask. 0 = Unmasked, 1 = Masked. |
irq_mask_set | Write | Masks IRQ for corresponding bits. 0 = No effect, 1 = Mask interrupt. | |
0x18 | irq_mask_clear | Read | N/A |
Write | Unmasks IRQ for corresponding bits. 0 = No effect, 1 = Unmask interrupt. | ||
0x1C | irq_mask_and_ack | Read | N/A |
Write | Acknowledge and mask the corresponding IRQ. 0 = No effect, 1 = Acknowledge and mask. | ||
0x20 | irq_edge_level | Read/Write | 0 = Level-sensitive IRQ, 1 = Edge-triggered IRQ. |
0x24 | irq_edge_sel | Read/Write | 0 = Use polarity to select edge, 1 = Either edge |
0x28 | irq_polarity | Read/Write | 0 = Active-low/falling edge, 1 = Active-high/rising edge. |
FPGA PWM
This system includes PWM that supports 10-bit duty/period, a 66.666666mhz input clock, and 12 values of input clock shift.
Linux supports this API through the /sys/ interface using file I/O. First export the pwm channel to enable it:
# Export PWM channel 0
echo 0 > /sys/class/pwm/pwmchip0/export
File | Description |
---|---|
/sys/class/pwm/pwmchip0/pwm0/period | Period in nanoseconds. Must be bigger than the duty cycle or writes will fail. Can only change when the pwm is disabled. |
/sys/class/pwm/pwmchip0/pwm0/duty_cycle | Duty cycle in nanoseconds. Can change at any time, must be less than period. |
/sys/class/pwm/pwmchip0/pwm0/enable | When 1, pwm is outputting. When 0, outputs idle state of the PWM. |
/sys/class/pwm/pwmchip0/pwm0/polarity | When "normal", idle high and duty cycle low. When "inversed", idle low and duty cycle high. A valid period must be set before this can be changed. |
For example, for a 50hz signal with 25% duty cycle:
# Set Period to 20ms
echo 20000000 > /sys/class/pwm/pwmchip0/pwm0/period
# Set duty cycle to 5ms
echo 5000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
# Enable PWM and output 50hz signal
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
# Duty cycle can be changed while it is enabled
echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
The Linux PWM API will attempt to arrive at the exact period at the cost of the duty cycle resolution. For the most possible duty cycle resolution use one of the max period ns values from the table below.
Shift | PWM Input Frequency (Hz) | Duty Cycle units (ns) | Max Period (ns) | Max Period (Hz) |
---|---|---|---|---|
0 | 66666666 | 15 | 15345 | 65167 |
1 | 33333333 | 30 | 30690 | 32583 |
2 | 16666666 | 60 | 61380 | 16291 |
3 | 8333333 | 120 | 122760 | 8145 |
4 | 4166666 | 240 | 245520 | 4072 |
5 | 2083333 | 480 | 491040 | 2036 |
6 | 1041666 | 960 | 982080 | 1018 |
7 | 520833 | 1920 | 1964161 | 509 |
8 | 260416 | 3840 | 3928330 | 254 |
9 | 130208 | 7860 | 7856660 | 127 |
10 | 65104 | 15360 | 15713320 | 63 |
11 | 32552 | 30720 | 31426640 | 31 |
If period is set to one of these values, the full 10 bits of duty cycle is available. Past that, the Linux API will use the closest available value. Debug output can be enabled with:
echo "file pwm-ts.c +p" > /sys/kernel/debug/dynamic_debug/control
If this is enabled, the kernel can output additional information after setting a frequency:
echo 0 > /sys/class/pwm/pwmchip0/export
# 10ms period:
echo 10000000 > /sys/class/pwm/pwmchip0/pwm0/period
# 5ms duty cycle:
echo 5000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
dmesg | tail
This will output:
[ 75.758146] ts-pwm 500001a8.mikro_pwm: cycle=1293661 shift=10 cnt=773 [ 75.758184] ts-pwm 500001a8.mikro_pwm: shift=10 cnt=773 duty_cnt=387
The last value in cnt indicates how much resolution is available for the duty cycle at this given period. In the best case there are 10 bits (0-2047) to specify duty cycle, but this above example is 0-773 to arrive at this particular period. You can determine the duty cycle increments with period / cnt. From the above example:
10000000 / 773 = 12936.61
The duty cycle can then be configured in increments of 12936ns. Smaller values will round to the closest value.
This PWM will allow a max speed of 79.2MHz / 3 = 26.4MHz, but this will sacrifice all of the available duty cycle except an on/50%/off. The slowest speed is highest divisor at 38hz.
While the Linux driver is recommended for most users, the PWM core is located at 0x28000400, each PWM are 0x20 registers apart.
Address | Bits | Description |
---|---|---|
0x00 (CONFIG) | 31:2 | Reserved |
1 | PWM Inversion (0 = Idle high, duty cycle low), (1 = Idle low, duty cycle high) | |
0 | PWM Enable (1 = Enable PWM output, 0 = Drive default state) | |
0x04 (PERIOD) | 31:10 | Reserved |
9:0 | PWM Period (0–1023 steps) | |
0x08 (DUTY) | 31:10 | Reserved |
9:0 | PWM Duty Cycle (0–1023 steps) | |
0x0C (SHIFT) | 31:12 | Reserved |
11:0 | Shift (Clock frequency = 66666666 / (1 >> shift)) |
FPGA XBAR
The FPGA XBAR controller allows rerouting some fpga pins between other pins. For example, the FPGA has connections to many of the CPU UARTs, SPI, and has several internal cores. This lets the users control which controllers are connected to certain pins. For example, the daughtercard header is all GPIO by default. The xbar allows routing the CPU's UART away from a RS-485 transceiver, and instead to this daughter card header. This does not make the daughter card header RS-485 levels, but just changes which controller drives these pins.
Under Linux the pin mapping can be controlled 2 ways, either through a device tree modification, or through debugfs in userspace.
For Userspace:
cd "/sys/kernel/debug/pinctrl/28002000.pinctrl-tsxbar-pinctrl/"
# echo a list of every output pin
cat pingroups
# echo a list of every function available:
cat pinmux-functions
# To see functions on a given pin:
cat pinmux-functions | grep MIKRO_TXD
# This returns:
##function 3: GPIO0_IO14, groups = [ MIKRO_TXD ]
##function 4: UART8_TXD, groups = [ MIKRO_TXD ]
# By default, MIKRO_TXD is a uart. To select the GPIO:
echo "MIKRO_TXD GPIO0_IO14" > pinmux-select
# To select the UART again:
echo "MIKRO_TXD UART8_TXD" > pinmux-select
For the device tree:
In the device tree, find the compatible = "technologic,ts9390-xbar";
node, and add pin mappings underneath that. See pinfunc-ts9390.h in the same path as the device tree for the available options. For example, this is how to define the mikrobus UART pins as GPIO or UART:
fpga_xbar: pinctrl@28002000 {
compatible = "technologic,ts4300-xbar";
reg = <0x2000 0x200>;
pinctrl_uart_mikrobus: mikrobusuartgrp {
xbar,pins = <
TS9390_PAD_MIKRO_TXD__UART8_TXD
TS9390_PAD_UART8_RXD__MIKRO_RXD
>;
};
pinctrl_gpio_mikrobus: mikrobusgpiogrp {
xbar,pins = <
TS9390_PAD_MIKRO_TXD__GPIO0_IO14
TS9390_PAD_MIKRO_RXD__GPIO0_IO15
>;
};
};
The mikrobus UART is the default, but this is how it would be explicitly mapped in the device tree:
&lpuart8 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart8 &pinctrl_uart_mikrobus>;
status = "okay";
};
The XBAR controller supports these muxing options:
Alt 0 | Alt 1 | Alt 2 | Alt 3 | Alt 4 | Alt 5 | Alt 6 | Alt 7 |
---|---|---|---|---|---|---|---|
Pin #00 (GPIO1_IO0), Default DIO_01 | |||||||
GPIO1_IO0 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #01 (GPIO1_IO1), Default DIO_02 | |||||||
GPIO1_IO1 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #02 (GPIO1_IO2), Default DIO_03 | |||||||
GPIO1_IO2 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #03 (GPIO1_IO3), Default DIO_04 | |||||||
GPIO1_IO3 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #04 (GPIO1_IO4), Default DIO_05 | |||||||
GPIO1_IO4 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #05 (GPIO1_IO5), Default DIO_06 | |||||||
GPIO1_IO5 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #06 (GPIO1_IO6), Default DIO_07 | |||||||
GPIO1_IO6 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #07 (GPIO1_IO7), Default DIO_08 | |||||||
GPIO1_IO7 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #08 (GPIO1_IO8), Default DIO_09 | |||||||
GPIO1_IO8 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #09 (GPIO1_IO9), Default DIO_10 | |||||||
GPIO1_IO9 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #10 (GPIO1_IO10), Default DIO_11 | |||||||
GPIO1_IO10 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #11 (GPIO1_IO11), Default DIO_12 | |||||||
GPIO1_IO11 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #12 (GPIO1_IO12), Default DIO_13 | |||||||
GPIO1_IO12 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #13 (GPIO1_IO13), Default DIO_14 | |||||||
GPIO1_IO13 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #14 (GPIO1_IO14), Default DIO_15 | |||||||
GPIO1_IO14 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #15 (GPIO1_IO15), Default DIO_16 | |||||||
GPIO1_IO15 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #16 (GPIO1_IO16), Default DIO_17 | |||||||
GPIO1_IO16 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #17 (GPIO1_IO17), Default DIO_18 | |||||||
GPIO1_IO17 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #18 (GPIO1_IO18), Default DIO_19 | |||||||
GPIO1_IO18 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #19 (GPIO1_IO19), Default DIO_20 | |||||||
GPIO1_IO19 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #20 (GPIO1_IO20), Default DIO_21 | |||||||
GPIO1_IO20 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #21 (GPIO1_IO21), Default DIO_22 | |||||||
GPIO1_IO21 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #22 (GPIO1_IO22), Default DIO_23 | |||||||
GPIO1_IO22 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #23 (GPIO1_IO23), Default DIO_24 | |||||||
GPIO1_IO23 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #24 (GPIO1_IO24), Default DIO_25 | |||||||
GPIO1_IO24 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #25 (GPIO1_IO25), Default DIO_26 | |||||||
GPIO1_IO25 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #26 (GPIO1_IO26), Default DIO_27 | |||||||
GPIO1_IO26 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #27 (GPIO1_IO27), Default DIO_28 | |||||||
GPIO1_IO27 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #28 (GPIO1_IO28), Default DIO_29 | |||||||
GPIO1_IO28 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #29 (GPIO1_IO29), Default DIO_30 | |||||||
GPIO1_IO29 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #30 (GPIO1_IO30), Default DIO_31 | |||||||
GPIO1_IO30 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #31 (GPIO1_IO31), Default DIO_32 | |||||||
GPIO1_IO31 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #32 (GPIO2_IO0), Default DIO_33 | |||||||
GPIO2_IO0 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #33 (GPIO2_IO1), Default DIO_34 | |||||||
GPIO2_IO1 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #34 (GPIO2_IO2), Default DIO_35 | |||||||
GPIO2_IO2 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #35 (GPIO2_IO3), Default DIO_36 | |||||||
GPIO2_IO3 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #36 (GPIO2_IO4), Default DIO_37 | |||||||
GPIO2_IO4 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #37 (GPIO2_IO5), Default DIO_38 | |||||||
GPIO2_IO5 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #38 (GPIO2_IO6), Default DIO_39 | |||||||
GPIO2_IO6 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #39 (GPIO2_IO7), Default DIO_40 | |||||||
GPIO2_IO7 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #40 (GPIO2_IO8), Default DIO_41 | |||||||
GPIO2_IO8 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #41 (GPIO2_IO9), Default DIO_42 | |||||||
GPIO2_IO9 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #42 (GPIO2_IO10), Default DIO_43 | |||||||
GPIO2_IO10 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #43 (GPIO2_IO11), Default DIO_44 | |||||||
GPIO2_IO11 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #44 (GPIO2_IO12), Default DIO_45 | |||||||
GPIO2_IO12 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #45 (GPIO2_IO13), Default DIO_46 | |||||||
GPIO2_IO13 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #46 (GPIO2_IO14), Default DIO_47 | |||||||
GPIO2_IO14 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #47 (GPIO2_IO15), Default DIO_48 | |||||||
GPIO2_IO15 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #48 (GPIO2_IO16), Default DIO_49 | |||||||
GPIO2_IO16 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #49 (GPIO2_IO17), Default DIO_50 | |||||||
GPIO2_IO17 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #50 (GPIO2_IO18), Default DIO_51 | |||||||
GPIO2_IO18 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #51 (GPIO2_IO19), Default DIO_52 | |||||||
GPIO2_IO19 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #52 (GPIO2_IO20), Default DIO_53 | |||||||
GPIO2_IO20 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #53 (GPIO2_IO21), Default DIO_54 | |||||||
GPIO2_IO21 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #54 (GPIO2_IO22), Default DIO_55 | |||||||
GPIO2_IO22 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #55 (GPIO2_IO23), Default DIO_56 | |||||||
GPIO2_IO23 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #56 (GPIO2_IO24), Default DIO_57 | |||||||
GPIO2_IO24 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #57 (GPIO2_IO25), Default DIO_58 | |||||||
GPIO2_IO25 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #58 (GPIO2_IO26), Default DIO_59 | |||||||
GPIO2_IO26 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #59 (GPIO2_IO27), Default DIO_60 | |||||||
GPIO2_IO27 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #60 (GPIO2_IO28), Default DIO_61 | |||||||
GPIO2_IO28 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #61 (GPIO2_IO29), Default DIO_62 | |||||||
GPIO2_IO29 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #62 (GPIO2_IO30), Default DIO_63 | |||||||
GPIO2_IO30 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #63 (GPIO2_IO31), Default DIO_64 | |||||||
GPIO2_IO31 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #64 (GPIO3_IO0), Default DIO_65 | |||||||
GPIO3_IO0 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #65 (GPIO3_IO1), Default DIO_66 | |||||||
GPIO3_IO1 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #66 (GPIO3_IO2), Default DIO_67 | |||||||
GPIO3_IO2 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #67 (GPIO3_IO3), Default DIO_68 | |||||||
GPIO3_IO3 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #68 (GPIO3_IO4), Default DIO_69 | |||||||
GPIO3_IO4 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #69 (GPIO3_IO5), Default DIO_70 | |||||||
GPIO3_IO5 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #70 (GPIO3_IO6), Default DIO_71 | |||||||
GPIO3_IO6 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #71 (GPIO3_IO7), Default DIO_72 | |||||||
GPIO3_IO7 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #72 (GPIO3_IO8), Default DIO_73 | |||||||
GPIO3_IO8 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #73 (GPIO3_IO9), Default DIO_74 | |||||||
GPIO3_IO9 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #74 (GPIO3_IO10), Default DIO_75 | |||||||
GPIO3_IO10 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #75 (GPIO3_IO11), Default DIO_76 | |||||||
GPIO3_IO11 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #76 (GPIO3_IO12), Default DIO_77 | |||||||
GPIO3_IO12 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #77 (GPIO3_IO13), Default DIO_78 | |||||||
GPIO3_IO13 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #78 (GPIO3_IO14), Default DIO_79 | |||||||
GPIO3_IO14 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #79 (GPIO3_IO15), Default DIO_80 | |||||||
GPIO3_IO15 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #80 (GPIO3_IO16), Default DIO_81 | |||||||
GPIO3_IO16 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #81 (GPIO3_IO17), Default DIO_82 | |||||||
GPIO3_IO17 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #82 (GPIO3_IO18), Default DIO_83 | |||||||
GPIO3_IO18 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #83 (GPIO3_IO19), Default DIO_84 | |||||||
GPIO3_IO19 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #84 (GPIO3_IO20), Default DIO_85 | |||||||
GPIO3_IO20 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #85 (GPIO3_IO21), Default DIO_86 | |||||||
GPIO3_IO21 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #86 (GPIO3_IO22), Default DIO_87 | |||||||
GPIO3_IO22 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #87 (GPIO3_IO23), Default DIO_88 | |||||||
GPIO3_IO23 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #88 (GPIO3_IO24), Default DIO_89 | |||||||
GPIO3_IO24 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #89 (GPIO3_IO25), Default DIO_90 | |||||||
GPIO3_IO25 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #90 (HIGHZ), Default SPI_4_CS_1V8# | |||||||
N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #91 (SPI_4_CS_1V8#), Default SPI4_CN2_CS# | |||||||
GPIO0_IO04 | SPI_4_CS_1V8# | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #92 (GPIO0_IO05), Default EN_USB_HOST_5V | |||||||
GPIO0_IO05 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #93 (GPIO0_IO06), Default OFF_BD_RESET | |||||||
GPIO0_IO06 | N/A | N/A | N/A | N/A | N/A | N/A | N/A |
Pin #94 (DIO_08), Default PMIC_RESET# | |||||||
N/A | DIO_08 | PMIC_RESET_DISABLED | N/A | N/A | N/A | N/A | N/A |
While the existing drivers should be used for any iomux interaction, this is the register documentation for interacting directly with the core.
Address | Bits | Read/Write | Description |
---|---|---|---|
0x0 | 31:24 | RW | PIN[n+3] FUNC_SEL |
23:16 | RW | PIN[n+2] FUNC_SEL | |
15:8 | RW | PIN[n+1] FUNC_SEL | |
7:0 | RW | PIN[n+0] FUNC_SEL |
This repeats for 0x0+ceil(N_PINS/4).
For each FUNC_SEL register:
Bits | Description |
---|---|
3 | HIGH_Z_EN, 1 = enable high-z, regardless of lower bits, 0 drive peripheral values |
2:0 | Value of 0-7 selects a function that drives oe+data |
FPGA IRQ
The FPGA has a single IRQ from the CPU, which it expands to its own IRQ capable cores. It is recommended to interact with GPIO underneath the FPGA which are IRQ capable rather than the IRQ core itself.
This core takes the 1 IRQ, and expands it to up to 32 IRQs. On the current design this is connected to:
IRQ | Description |
---|---|
0 | FPGA GPIO 0 |
1 | FPGA GPIO 1 |
2 | FPGA GPIO 2 |
Most physical IRQ capable pins are connected underneath the GPIO cores which provide flexibility for IRQ type, polarity, acks, etc to not miss edges.
We recommend to use the existing Linux BSP which implements drivers for this already ("technologic,fpga-irqc"), but this core uses this register map.
Address | Name | Access | Description |
---|---|---|---|
0x00 | irq_status | R | Shows active and unmasked interrupts. 0 = Interrupt inactive or masked. 1 = Interrupt active and unmasked. |
W | *Not applicable* | ||
0x04 | irq_mask_set | R | Returns mask register; 1 = masked, 0 = unmasked. |
W | **Write 1** to a bit position to enable that interrupt (sets the corresponding bit). | ||
0x08 | irq_mask_clr | R | N/A |
W | **Write 1** to a bit position to unmask that interrupt. |
FPGA Updates
For most Linux users, the FPGA can be updated with:
curl -sSL http://files.embeddedts.com/ts-socket-macrocontrollers/ts-4300-linux/fpga/update-fpga.sh | sh
Then reboot.
This FPGA supports multiple application loads. On startup, it always starts the first image which is a bootloader that is capable of rewriting the FPGA's flash. If there is no update, it sets a register that switches the FPGA to the second image which contains the common functionality like GPIO, IRQs, etc.
U-boot checks checks for a valid 'fit' image in the eMMC boot 1 partition (mmcblk0boot1) at offset 0x280000. If it finds this valid update, then before switching out of the FPGA bootloader it rewrites the second application load. This take approximately 40 seconds. After writing, it then erases the region of mmcblk0boot1 containing the update, and then sets the register in the bootloader that updates the FPGA.
This update process is designed so the typical case of updating the application load is safe even in the case of sudden power loss. The FPGA bootloader is in a separate erase block of the FPGA's internal flash, so if we are interrupted in the middle of erasing/writing flash it only affects the application load. On the next start up, since the update has not been completed and still exists on mmcblk0boot1, it will restart the update and try again until it succeeds.
GPIO
The i.MX93 CPU and FPGA GPIOs are exposed using a kernel character device. This interface provides a set of files and directories for interacting with GPIO which can be used from any language that interact with special files in linux using ioctl() or similar. For our platforms, we pre-install the "libgpiod" library and binaries. Documentation on these tools can be found here. This section only covers using these userspace tools and does not provide guidance on using the libgpiod library in end applications. Please see the libgpiod documentation for this purpose.
A user with suitable permissions to read and write /dev/gpiochip* files can immediately interact with GPIO pins. For example, to see read the push switch:
gpioget $(gpiofind PUSH_SW#)
Multiple pins in the same chip can be read simultaneously by passing multiple pin numbers separated by spaces.
To write to a pin, the gpioset
command is used. For example, to set Relay 1:
gpioset $(gpiofind EN_RELAY_1)=1
Multiple pins in the same chip can be set simultaneously by passing multiple pin=value pairs separated by spaces.
If a call with gpioset
or gpioget
fails with "Device or resource busy," that means that specific GPIO is claimed by another device. The command cat /sys/kernel/debug/gpio
can be used to get a list of all of the system GPIO and what has claimed them.
The gpiomon
tool can be used to monitor pins for changes to GPIOs that generate interrupts.
In the following table, gpiochips 0 through 2 are on the FPGA.
gpiochip 3 is on the CPU, but none of the pins are available as GPIOs.
Because this numbering is subject to change, it is advisable to use the gpiofind
command to look it up by its label, as shown in the usage examples above and elsewhere in this manual.
Schematic Net Name | Direction | Bank | Line | Location |
---|---|---|---|---|
EN_GREEN_LED# | Out | 4 | 0 | Green LED |
EN_RED_LED# | Out | 4 | 1 | Red LED |
SPI4_CN2_CS# | Bidir | 4 | 4 | CN2_065 |
EN_USB_HOST_5V | Bidir | 4 | 5 | CN1_004 |
OFF_BD_RESET# | Bidir | 4 | 6 | CN1_009 |
SEL_LVDS | Out | 4 | 7 | Onboard |
SPI_4_CS_1V8# | Bidir | 4 | 8 | Onboard |
DIO_01 | Bidir | 5 | 0 | CN2_060 |
DIO_02 | Bidir | 5 | 1 | CN2_062 |
DIO_03 | Bidir | 5 | 2 | CN2_064 |
DIO_04 | Bidir | 5 | 3 | CN2_066 |
DIO_05 | Bidir | 5 | 4 | CN2_068 |
DIO_06 | Bidir | 5 | 5 | CN2_070 |
DIO_07 | Bidir | 5 | 6 | CN2_072 |
DIO_08 | Bidir | 5 | 7 | CN1_002 |
DIO_09 | Bidir | 5 | 8 | CN1_006 |
DIO_10 | Bidir | 5 | 9 | CN1_008 |
DIO_11 | Bidir | 5 | 10 | CN1_010 |
DIO_12 | Bidir | 5 | 11 | CN1_012 |
DIO_13 | Bidir | 5 | 12 | CN1_014 |
DIO_14 | Bidir | 5 | 13 | CN1_018 |
DIO_15 | Bidir | 5 | 14 | CN1_020 |
DIO_16 | Bidir | 5 | 15 | CN1_022 |
DIO_17 | Bidir | 5 | 16 | CN1_024 |
DIO_18 | Bidir | 5 | 17 | CN1_026 |
DIO_19 | Bidir | 5 | 18 | CN1_028 |
DIO_20 | Bidir | 5 | 19 | CN1_030 |
DIO_21 | Bidir | 5 | 20 | CN1_032 |
DIO_22 | Bidir | 5 | 21 | CN1_034 |
DIO_23 | Bidir | 5 | 22 | CN1_038 |
DIO_24 | Bidir | 5 | 23 | CN1_040 |
DIO_25 | Bidir | 5 | 24 | CN1_042 |
DIO_26 | Bidir | 5 | 25 | CN1_044 |
DIO_27 | Bidir | 5 | 26 | CN1_046 |
DIO_28 | Bidir | 5 | 27 | CN1_048 |
DIO_29 | Bidir | 5 | 28 | CN1_099 |
DIO_30 | Bidir | 5 | 29 | CN1_052 |
DIO_31 | Bidir | 5 | 30 | CN1_054 |
DIO_32 | Bidir | 5 | 31 | CN1_056 |
DIO_33 | Bidir | 6 | 0 | CN1_058 |
DIO_33 | Bidir | 6 | 0 | CN1_058 |
DIO_34 | Bidir | 6 | 1 | CN1_060 |
DIO_35 | Bidir | 6 | 2 | CN1_064 |
DIO_36 | Bidir | 6 | 3 | CN1_066 |
DIO_37 | Bidir | 6 | 4 | CN1_068 |
DIO_38 | Bidir | 6 | 5 | CN1_070 |
DIO_39 | Bidir | 6 | 6 | CN1_072 |
DIO_40 | Bidir | 6 | 7 | CN1_074 |
DIO_41 | Bidir | 6 | 8 | CN1_076 |
DIO_42 | Bidir | 6 | 9 | CN1_078 |
DIO_43 | Bidir | 6 | 10 | CN1_080 |
DIO_44 | Bidir | 6 | 11 | CN1_082 |
DIO_45 | Bidir | 6 | 12 | CN1_084 |
DIO_46 | Bidir | 6 | 13 | CN1_086 |
DIO_47 | Bidir | 6 | 14 | CN1_088 |
DIO_48 | Bidir | 6 | 15 | CN1_090 |
DIO_49 | Bidir | 6 | 16 | CN1_092 |
DIO_50 | Bidir | 6 | 17 | CN1_094 |
DIO_51 | Bidir | 6 | 18 | CN1_096 |
DIO_52 | Bidir | 6 | 19 | CN1_098 |
DIO_53 | Bidir | 6 | 20 | CN1_100 |
DIO_54 | Bidir | 6 | 21 | CN1_011 |
DIO_55 | Bidir | 6 | 22 | CN1_013 |
DIO_56 | Bidir | 6 | 23 | CN1_017 |
DIO_57 | Bidir | 6 | 24 | CN1_019 |
DIO_58 | Bidir | 6 | 25 | CN1_021 |
DIO_59 | Bidir | 6 | 26 | CN1_023 |
DIO_60 | Bidir | 6 | 27 | CN1_025 |
DIO_61 | Bidir | 6 | 28 | CN1_027 |
DIO_62 | Bidir | 6 | 29 | CN1_031 |
DIO_63 | Bidir | 6 | 30 | CN1_033 |
DIO_64 | Bidir | 6 | 31 | CN1_035 |
DIO_65 | Bidir | 7 | 0 | CN1_037 |
DIO_65 | Bidir | 7 | 0 | CN1_037 |
DIO_66 | Bidir | 7 | 1 | CN1_039 |
DIO_67 | Bidir | 7 | 2 | CN1_041 |
DIO_68 | Bidir | 7 | 3 | CN1_043 |
DIO_69 | Bidir | 7 | 4 | CN1_045 |
DIO_70 | Bidir | 7 | 5 | CN1_049 |
DIO_71 | Bidir | 7 | 6 | CN1_051 |
DIO_72 | Bidir | 7 | 7 | CN1_053 |
DIO_73 | Bidir | 7 | 8 | CN1_055 |
DIO_74 | Bidir | 7 | 9 | CN1_057 |
DIO_75 | Bidir | 7 | 10 | CN1_059 |
DIO_76 | Bidir | 7 | 11 | CN1_061 |
DIO_77 | Bidir | 7 | 12 | CN1_063 |
DIO_78 | Bidir | 7 | 13 | CN1_065 |
DIO_79 | Bidir | 7 | 14 | CN1_067 |
DIO_80 | Bidir | 7 | 15 | CN1_073 |
DIO_81 | Bidir | 7 | 16 | CN1_077 |
DIO_82 | Bidir | 7 | 17 | CN1_079 |
DIO_83 | Bidir | 7 | 18 | CN1_081 |
DIO_84 | Bidir | 7 | 19 | CN1_083 |
DIO_85 | Bidir | 7 | 20 | CN1_085 |
DIO_86 | Bidir | 7 | 21 | CN1_087 |
DIO_87 | Bidir | 7 | 22 | CN1_089 |
DIO_88 | Bidir | 7 | 23 | CN1_091 |
DIO_89 | Bidir | 7 | 24 | CN1_093 |
DIO_90 | Bidir | 7 | 25 | CN1_097 |
I2C
This product uses two CPU I2C busses for on-board ICs, one I3C-capable bus for offboard.
Device | Address | Description |
---|---|---|
/dev/i2c-0 | Unused | This bus is brought out to CN2_28 (SCL) / CN2_30 (SDA) |
/dev/i2c-1 | 0x25 | NXP PMIC |
/dev/i2c-3 | ||
0x2e | TPM [1] | |
0x54 | Wizard µC | |
0x60 | #RTC |
- ↑ On Rev P3 and later
I3C
LCD Interface
The i.MX93 includes an LCD Interface controller with a simple framebuffer, which can then be passed to LVDS or MIPI-DSI.
LCD Output Mux
The TS-4300 includes a high speed mux controlled by SEL_LVDS. This allows the TS-SOCKET to provide both LVDS and MIPI-DSI on the odd pins CN2_41 through CN2_61.
This can be selected in device tree by specifying the mux state in your MIPI or LVDS nodes:
/* Select MIPI-DSI */
mux-states = <&video_mux 1>;
...
/* Select LVDS */
mux-states = <&video_mux 0>;
SEL_LVDS Value | Function | TS-Socket pin |
---|---|---|
0 | CPU_LVDS_D1_P | CN2_41 |
CPU_LVDS_D1_M | CN2_43 | |
CPU_LVDS_D0_P | CN2_47 | |
CPU_LVDS_D0_M | CN2_49 | |
CPU_LVDS_D2_P | CN2_53 | |
CPU_LVDS_D2_M | CN2_55 | |
CPU_LVDS_CLK_P | CN2_59 | |
CPU_LVDS_CLK_M | CN2_61 | |
1 | CPU_MIPI_D1_P | CN2_41 |
CPU_MIPI_D1_M | CN2_43 | |
CPU_MIPI_D0_P | CN2_47 | |
CPU_MIPI_D0_M | CN2_49 | |
CPU_MIPI_D3_P | CN2_53 | |
CPU_MIPI_D3_M | CN2_55 | |
CPU_MIPI_D2_P | CN2_59 | |
CPU_MIPI_D2_M | CN2_61 |
LVDS
MIPI-DSI
The i.MX93 includes a MIPI-DSI (DPHY) which, using 4 lanes provides support for up to 2048x1080 pixels at 60hz, and 24 bpp. Refer to the CPU Reference manual's chapters on MIPI-DSI / MIPI-DSI DPHY for more details.
LEDs
There are two LEDS on the TS-4300 that may be controlled by the user through the sysfs interface. These are colored red and green.
To turn an LED on, write a 1 to 'brightness'. To turn it off again, write a 0.
# Example: Turn on the Red LED...
echo 1 > /sys/class/leds/red:status/brightness
# Turn it off again...
echo 0 > /sys/class/leds/red:status/brightness
# Make the green LED act as a heartbeat
echo heartbeat > /sys/class/leds/green:power/trigger
A number of triggers are also available for each LED, including timers, disk activity, and heartbeat. These allow the LEDs to represent various system activities as they occur. See the kernel LED documentation for more information on triggers and general use of LED class devices.
MicroSD Interface
NPU
The i.MX93 includes an ARM Ethos-U65 NPU to support accelerating neural networks for AI applications.
Features:
- 256 MACs operating up to 1GHz and 2 OPS/MAC
- Targets 8-bit and 16-bit integer RNN
- Handles 8-bit weights
The initial kernels for i.MX93-based products are based on NXP's repository, where NPU support only exists via the M33 co-processor. NXP provides separate firmware for operating the M33 as a standalone model-runner or as a Tensorflow "delegate". This section only discusses the latter method of using the NPU. The Tensorflow delegate takes full control of the M33 and enables Linux's use of the NPU as a machine-learning coprocessor. It shows up as /dev/ethosu0 when kernel-level support is present, activated by compile-time configuration parameters and device tree entries. Additional required software components are as follows:
Component/Repository | Description |
ethos-u-firmware | Firmware binary. Source may be downloaded from NXP as part of their MCUExpresso BSP. |
ethos-u-driver-stack-imx | User-space driver and libraries that go through /dev/ethosu0 on NXP processors |
tflite-ethosu-delegate-imx | This is how Tensorflow knows how to use Ethos-U on NXP processors |
nxp-imx/tensorflow-imx | NXP's fork of Tensorflow, including Tensorflow Lite, that is needed on the i.MX93 |
nxp-imx/ethos-u-vela.git | NXP's Vela fork is required in order to translate Tensorflow Lite models for the Ethos-U |
Details on these, as well as on how to use NXP's software support via Yocto, can be found in their i.MX Machine Learning User's Guide.
It should be noted that every one of the above repositories has a kernel release-specific branch. Failures, either when building or at runtime, are common when intermixing releases or when not using a branch that is correctly matched to the kernel in use.
Our standard Debian releases currently include kernel support for Ethos-U, but the additional software listed above must be downloaded and built in order to make use of the NPU.
To make this as pain-free as possible, we provide a script that downloads, builds, and installs all of these components and their dependencies atop our headless or X11 distributions. The script must be executed on the i.MX93 system running the distribution, and requires a high-speed Internet connection. Expect it to take up to three hours.
wget http://files.embeddedts.com/ts-socket-macrocontrollers/ts-4300-linux/scripts/install-npu-software.sh
chmod +x install-npu-software.sh
./install-npu-software.sh
When complete, a much shorter script can be used to verify the installation by downloading and executing this demo (similar to one that appears in the ML User's Guide):
wget http://files.embeddedts.com/ts-socket-macrocontrollers/ts-4300-linux/scripts/run-hopper-example.sh
chmod +x run-hopper-example.sh
./run-hopper-example.sh
The script downloads a Tensorflow model, runs Vela to prepare it for Ethos-U, and then performs an inference task using a test image and label choices that come with Tensorflow.
SILO
Sleep Mode
SPI
This platform brings out the one "LPSPI4" controller from the i.MX93.
This is connected to:
- Onboard ADC
- Mikrobus SPI
- /dev/spidev1.1
- Daughtercard Header (When selected through #FPGA XBAR)
- /dev/spidev1.2
See SPI MUX for more information about how this LPSPI4 is mapped to other pins.
The /dev/spidev* devices can be accessed from Linux. See the kernel spidev documentation for more information on interfacing with the SPI peripherals from C.
Other languages also have bindings to interface with spidev:
UARTs
USB
Watchdog
WiFi
Specifications
IO specifications
Power Consumption
Power Input Specifications
TS-4300 Power Input Specifications
External Interfaces
TS-Socket
USB-C Console Connector
Revisions and Changes
PCB Revisions
Revision | Changes |
---|---|
P2 |
|
U-Boot Revisions
Depending on context, you can determine your U-Boot revision in one of several ways:
1. The U-Boot build date can be viewed as the first line of USB console output when the unit is powered on. For example:
U-Boot 2016.03-00408-gd450758c91 (Oct 10 2019 - 11:59:08 -0700) CPU: Freescale i.MX6UL rev1.2 at 396 MHz ...
2. U-Boot has a version
command that outputs similar version information to what is shown above.
3. At a Linux shell, the following command prints the version strings of any U-Boot and/or SPL image that is present in eMMC:
strings /dev/mmcblk0boot0 | grep '^U-Boot .*(.*)'
The output is the same string(s) that will be printed on the console at board startup. TS-4300 U-Boot Changelog
FPGA Revisions
To determine the version of the FPGA on a TS-4300, see the startup output in U-Boot:
U-Boot 2024.04-00047-gd6a3cd9d75c-dirty (Mar 07 2025 - 21:03:22 +0000) Reset Status: POR CPU: NXP i.MX93(52) Rev1.1 A55 at 1700 MHz CPU: Extended Industrial temperature grade (-40C to 125C) at 32C DRAM: 1 GiB Model: embeddedTS TS-4300 Core: 200 devices, 25 uclasses, devicetree: fit MMC: FSL_SDHC: 0, FSL_SDHC: 1 Loading Environment from MMC... OK In: serial Out: serial Err: serial BuildInfo: - ELE firmware version 1.2.0-38f309fe switch to partitions #0, OK mmc0(part 0) is current device FPGA Bootloader: v0.1.2 FPGA TS-4300: v0.2.2 flash target is MMC:0
In this case, the bootloader is v0.1.2, and the FPGA application is v0.2.2.
See the FPGA Updates section for more details on updating to the latest.
Date | Version | Description |
---|---|---|
2025-05-21 | v0.2.2 |
|
2025-09-17 | v0.2.3 |
|
Wizard Firmware Revisions
Software Images
Debian Changelog
Product Notes
FCC Advisory
This equipment generates, uses, and can radiate radio frequency energy and if not installed and used properly (that is, in strict accordance with the manufacturer's instructions), may cause interference to radio and television reception. It has been type tested and found to comply with the limits for a Class A digital device in accordance with the specifications in Part 15 of FCC Rules, which are designed to provide reasonable protection against such interference when operated in a commercial environment. Operation of this equipment in a residential area is likely to cause interference, in which case the owner will be required to correct the interference at his own expense.
If this equipment does cause interference, which can be determined by turning the unit on and off, the user is encouraged to try the following measures to correct the interference:
Reorient the receiving antenna. Relocate the unit with respect to the receiver. Plug the unit into a different outlet so that the unit and receiver are on different branch circuits. Ensure that mounting screws and connector attachment screws are tightly secured. Ensure that good quality, shielded, and grounded cables are used for all data communications. If necessary, the user should consult the dealer or an experienced radio/television technician for additional suggestions. The following booklets prepared by the Federal Communications Commission (FCC) may also prove helpful:
How to Identify and Resolve Radio-TV Interference Problems (Stock No. 004-000-000345-4) Interface Handbook (Stock No. 004-000-004505-7) These booklets may be purchased from the Superintendent of Documents, U.S. Government Printing Office, Washington, DC 20402.
Limited Warranty
See our Terms and Conditions for more details.
WARNING: | Setting any of the eMMC's write-once registers (e.g. enabling enhanced area and/or write reliability) will immediately void ALL of our return policies and replacement warranties. This includes but is not limited to: the 45-day full money back evaluation period; any returns outside of the 45-day evaluation period; warranty returns within the 1 year warranty period that would require SBC replacement. Our 1 year limited warranty still applies, however it is at our discretion to decide if the SBC can be repaired, no warranty replacements will be provided if the OTP registers have been written. |
Trademarks
Arm and Cortex are registered trademarks of Arm Limited (or its subsidiaries) in the US and/or elsewhere.