MIDAS Ultrasound v2.0 Demo

From Texas Instruments Wiki
Jump to: navigation, search

MIDAS Versions

Please note that the newer MIDAS Ultrasound version 4.0 is available now.

Please see the main MIDAS wiki page to compare different versions of MIDAS and see what suits your use-case the best.

Overview

Texas Instruments' Medical Imaging Demo Application Starter (MIDAS) illustrates the integration of key medical imaging algorithm modules on Texas Instruments (TI) DSPs and System-on-Chips. This version incorporates the Ultrasound v2.0 demo which integrates components from the ultrasound signal chain including the B-mode Processing Unit (BPU), the Doppler Processing Unit (DPU) and the Scan Conversion Unit (SCU) from the Embedded Processor Software Toolkit for Medical Imaging Applications v2.0, on the TI OMAP3530 Mistral EVM. In addition, MIDAS showcases the use of Qt and tslib to create a touchscreen-based Graphical User Interface (GUI) that allows users to interact with the ARM and DSP in real-time.

The TI OMAP3530 consists of a 720MHz ARM Cortex A-8 general processor and a 520MHz C64x+ DSP core, which together provide a powerful, yet power-efficient solution to handle system controller and back-end processing functions in diagnostic ultrasound imaging systems.

This document covers various aspects of MIDAS, including a discussion on the software design, a guide to create a bootable SD card that plugs into your EVM and runs MIDAS, as well as step-by-step instructions to obtain the MIDAS source code, and setup your development environment to build and run the demo.

The figure below showcases the function of the ARM and DSP cores for MIDAS:

Midas algorithm overview.png

Here is a snapshot showing the MIDAS Ultrasound demo in 'Imaging' mode:

MIDAS Ultrasound v2.0 running on TI OMAP3530 Mistral EVM

GForge Project Page

We use the GForge portal to provide access to source files, SD card images and other related information. The portal also allows us to collaborate with the community.

Our MIDAS project page is located at https://gforge.ti.com/gf/project/med_ultrasound/

Requirements

  • TI OMAP3530 Mistral EVM with at least 256 MB DDR (processor modules Rev C or up)
  • EVM 5V Power Supply (included with EVM)

If building the demo from source:

  • Linux workstation
  • RS-232 cable (included with EVM)
  • Ethernet cable (included with EVM)

If creating a bootable SD card:

  • Linux/Windows workstation
  • 2GB SD card
  • SD card reader

Notation

1. Commands are preceded by prompts that indicate the environment where the command is to be typed. For example:

host $
Indicates command to be typed into the shell window of the host Linux workstation.
EVM #
Indicates commands to be typed into the U-Boot shell in a console window connected to the EVM board’s serial port.
target $
Indicates commands to be typed into the Linux shell in the terminal window connected to the EVM board's serial port.

2. <> represents a variable that can change from one development environment to another. For example, /home/<username>

3. ~ represents the user's home directory, i.e. /home/<username>

4. You are free to use any Linux distribution for your host machine but please note that commands on this page have been tested on a host machine with the Ubuntu Linux distribution

5. Some basic Linux knowledge is assumed

Troubleshooting

If you run into any build errors or issues through the course of this setup, we urge you to please first go through the Common Gotchas which are at the end of most sections on this page. If you do not find a solution, please post it to http://e2e.ti.com and we will try to answer your questions on there. Thanks!

Build Process Overview

Here's an overview of the steps you will take to setup your development environment and build MIDAS from source. Alternately if you are just interested in a bootable SD card image, skip to the 'SD Card' section of this document.

MIDAS Build Process Overview

PSP and DVSDK Setup

MIDAS is built on top of Texas Instrument's Linux Digital Video Software Development Kit (DVSDK) for OMAP3530 package, which incorporates all software components required to kickstart application development including the Linux kernel, U-boot, and TI provided framework components like Codec Engine and DMAI.

1. For the PSP and DVSDK 3.00.02.44 software installers click here

2. Please note the important guidelines below and then follow instructions here to setup the PSP and DVSDK software

Important:
  • Make sure to follow the guide's instructions to rebuild u-boot (and flash it to NAND), Linux kernel, DVSDK, and NFS since the pre-existing binary images may not be configured for the OMAP3530
  • Before you proceed to build the DVSDK software, there is a software bug in DVSDK that will be fixed in later versions, but needs to be manually corrected for now.
File ~/dvsdk_3_02_00_44/dmai_2_00_01_04/packages/ti/sdo/dmai/linux/Display_v4l2.c

Function: static Int cleanup (Display_Handle hDisplay)

Current:
if (munmap(Buffer_getUserPtr(hDispBuf), Buffer_getSize(hDispBuf)) == -1) {
   Dmai_err1("Failed to unmap capture buffer%d\n", bufIdx);
   ret = Dmai_EFAIL;
}

Fix:
if (munmap(Buffer_getUserPtr(hDispBuf), hDisplay->bufDescs[bufIdx].v4l2buf.length) == -1) {
Dmai_err1("Failed to unmap capture buffer%d\n", bufIdx);
ret = Dmai_EFAIL;
}
  • To export the ~/workdir/filesys for NFS, you need to install the nfs-kernel-server package as follows:
host $ sudo apt-get install nfs-kernel-server
  • It is assumed through this guide that DVSDK and PSP software is stored at ~. For example, ~/dvsdk_3_00_02_44 and ~/OMAP35x-PSP-SDK-02.01.03.11

3. Once you have completed step 2, you should have the following pieces of software setup on your linux host:

  • DVSDK at ~/dvsdk_3_00_02_44
  • NFS at ~/workdir/filesys
  • PSP at ~/workdir/opt/.
  • Toolchain at ~/toolchain/arm-2009q1

4. Setup the EVM board to boot via TFTP using NFS filesystem

  • Click here for instructions to setup a TFTP client on your host.
  • Copy the kernel 'uImage' you built during 'PSP and DVSDK setup' and place it in your TFTP directory on the host (Eg: /var/lib/tftpboot)
  • On your EVM, set the following parameters at the u-boot prompt after you abort the automatic boot sequence. Note that for Ubuntu 10.04 up, you need to specify the entire path for the bootfile, as in '/var/lib/tftpboot/uImage' instead of just 'uImage.' The rootpath is typically '/home/<username>/workdir/filesys'
OMAP3EVM # setenv bootcmd 'dhcp;bootm'
OMAP3EVM # setenv serverip <ip addr of tftp server>
OMAP3EVM # setenv bootfile <name of kernel image>
OMAP3EVM # setenv rootpath <root directory to mount>
OMAP3EVM # setenv nfshost <ip addr of nfs host>
OMAP3EVM # setenv bootargs console=ttyS0,115200n8 noinitrd rw ip=dhcp root=/dev/nfs nfsroot=$(nfshost):$(rootpath),nolock
mem=99M@0x80000000 mem=128M@0x88000000 mpurate=600 omapfb.rotate=1 omapfb.rotate_type=1 omap_vout.vid1_static_vrfb_alloc=y 

Touchscreen Setup

MIDAS utilizes the tslib library for touchscreen support on the OMAP3530 EVM. This section will guide you through setting up tslib.

1. Install required packages on host

host $ sudo apt-get install autogen autoconf libtool

2. For cross-compiling, define environment variables in ~/.bashrc on host as:

export CC=/home/<username>/arm-2009q1/bin/arm-none-linux-gnueabi-gcc
export CXX=/home/<username/arm-2009q1/bin/arm-none-linux-gnueabi-g++
export CONFIG_SITE=omap3evm.autogen

3. Download the source files for tslib from here and save it at ~

4. Untar the source

host $ cd ~host $ tar xjf tslib-1.0.tar.bz2

5. Configure tslib

host $ cd ~/tslib-1.0
host $ ./autogen.sh
host $ ./configure --prefix=/home/<username>/workdir/filesys --host=arm-linux-gnu

6. Comment the line ‘AC_FUNC_MALLOC’ in ~/tslib-1.0/configure.ac

7. Comment ‘#define malloc rpl_malloc’ in ~/tslib-1.0/config.h if it exists

8. Build and install tslib

host $ make
host $ make install

9. Edit ~/workdir/filesys/etc/ts.conf to use linux layer event interface by uncommenting ‘module_raw input’

10. Modify the device’s startup script to export the required touchscreen related environment variables.

    Add these lines to the file ~/development/workdir/filesys/etc/profile:

export QWS_MOUSE_PROTO='Tslib:/dev/input/event1'
export TSLIB_CALIBFILE='/etc/pointercal'
export TSLIB_CONFFILE='/etc/ts.conf'
export TSLIB_CONSOLEDEVICE='none'
export TSLIB_FBDEVICE='/dev/fb0'
export TSLIB_PLUGINDIR='/lib/ts'
export TSLIB_TSDEVICE='/dev/input/event1'

11. Remember to run ‘ts_calibrate’ on the target the first time you start the board, to calibrate the touch screen.

Qt Setup

MIDAS utilizes the Qt application and UI framework to create the Graphical User Interface for MIDAS. The GUI allows users to interact with the application via the touchscreen and modify processing parameters for algorithms running on the DSP, start-stop imaging, change modes and see results of these actions in real-time. This section describes the setup of Qt libraries and the Qt SDK both components essential for Qt UI development.

Qt Libraries

1. Download the Qt libraries 4.6.2 for embedded Linux from here, save it in ~, and extract the source as:

host $ cd ~
host $ gunzip qt-everywhere-opensource-src-4.6.2.tar.gz
host $ tar xvf qt-everywhere-opensource-src-4.6.2.tar

2. Important: Ensure that Qt uses the host g++ for compiling the Qt libraries. To do this, check your ~/.bashrc file to ensure that CC, CXX variables are not set for cross-compile, i.e. comment any custom definitions as below if present by adding the # sign, as:

#export CC=~/toolchain/arm-2009q1/bin/arm-none-linux-gnueabi-gcc
#export CXX=~/toolchain/arm-2009q1/bin/arm-none-linux-gnueabi-g++

   Run the following command to reset paths.

host $ source ~/.bashrc

   It is recommended that you close this command window and open a new one before proceeding to ensure environment variables have been picked up.
   NOTE: If you see an error with this please see Gotcha 1 for a common mistake in this step.

3. Edit ~/qt-everywhere-opensource-src-4.6.2/mkspecs/common/linux.conf and add the “-lts” option to QMAKE_LIBS_THREAD as:

QMAKE_LIBS_THREAD     = -lpthread -lts

4. Create an omap3 qmake configuration as follows:

host $ cd ~/qt-everywhere-opensource-src-4.6.2/mkspecs/qws/
host $ cp -r ./linux-arm-g++ ./linux-omap3-g++
host $ gedit ./linux-omap3-g++/qmake.conf

    Replace the text in qmake.conf with the following. Make sure to change <username> as per your host:

# qmake configuration for building with arm-linux-g++

include(../../common/g++.conf)
include(../../common/linux.conf)
include(../../common/qws.conf)

# modifications to g++.conf

# To include ts-lib
QMAKE_INCDIR += /home/<username>/workdir/filesys/include
QMAKE_LIBDIR += /home/<username>/workdir/filesys/lib

#Compiler Flags to take advantage of the ARM architecture
QMAKE_CFLAGS_RELEASE = -O3 -march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp
QMAKE_CXXFLAGS_RELEASE= -O3 -march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp
QMAKE_CC = arm-none-linux-gnueabi-gcc
QMAKE_CXX = arm-none-linux-gnueabi-g++
QMAKE_LINK = arm-none-linux-gnueabi-g++
QMAKE_LINK_SHLIB = arm-none-linux-gnueabi-g++

# modifications to linux.conf
QMAKE_AR = arm-none-linux-gnueabi-ar cqs
QMAKE_OBJCOPY = arm-none-linux-gnueabi-objcopy
QMAKE_STRIP = arm-none-linux-gnueabi-strip

load(qt_config)

5. Configure qmake and build Qt libraries. 

host $ cd ~/qt-everywhere-opensource-src-4.6.2
host $ ./configure -prefix /opt/qt -embedded arm -platform /qws/linux-x86-g++ -xplatform /qws/linux-omap3-g++ -depths 16,24,32 \
-no-mmx -no-3dnow -no-sse -no-sse2 -no-glib -no-cups -no-largefile -no-accessibility -no-openssl -no-gtkstyle -qt-mouse-pc \
-qt-mouse-linuxtp -qt-mouse-linuxinput -plugin-mouse-linuxtp -plugin-mouse-pc -fast -qt-mouse-tslib
host $ make
host $ make install INSTALL_ROOT=/home/<username>/workdir/filesys

    Important:

  • For a 64-bit host machine, use "-platform /qws/linux-x86_64-g++" instead of "-platform /qws/linux-x86-g++."
  • Make sure to change <username> as per your host.
  • In case you need to run configure again with different settings, first do a 'make confclean' followed by your './configure...' command.
  • This build can take upto a few hours to complete.

6. Add 'qmake' to the host's PATH. Edit ~/.bashrc and add the following lines:

export PATH=/home/<username>/qt-everywhere-opensource-src-4.6.2/bin:$PATH
export QMAKESPEC=/home/<username>/qt-everywhere-opensource-src-4.6.2/mkspecs/qws/linux-omap3-g++/

    Run the following command to reset paths

host $ source ~/.bashrc 

Common Gotchas

  • Gotcha 1
Error message 1:
~/qt-everywhere-opensource-src-4.6.2/bin/qmake: 1: Syntax error: word unexpected (expecting ")") make:
*** No targets specified and no makefile found.  Stop.”
Error message 2:
project.o: could not read symbols: File in wrong format
collect2: ld returned 1 exit status
make: ***
[~/qt-everywhere-opensource-src-4.6.2/bin/qmake] Error 1


Solution:

This is typically due to a compiler mismatch and you have most likely skipped step 2. Ensure that when you give the ./configure command you see:
“Creating qmake. Please wait... g++ -c -o ……”
And not:
“Creating qmake. Please wait…arm-none-linux-gnueabi-g++ -c -o

Important: If you have first accidently cross-compiled, before giving the configure command again, it is important that you go to your ~/qt-everywhere-opensource-src-4.6.2/qmake directory and delete all the *.o files, since if you try and continue the build without cleaning these out, it will try to link in these cross-compiled files. Do this as:
host $ cd ~/qt-everywhere-opensource-src-4.6.2/qmake
host $ rm *.o
  • Gotcha 2
Error message: ~/qt-everywhere-opensource-src-4.6.2/lib/libQtGui.so: undefined reference to `ts_read_raw'
~/qt-everywhere-opensource-src-4.6.2/lib/libQtGui.so: undefined reference to `ts_open'
~/qt-everywhere-opensource-src-4.6.2/lib: undefined reference to `ts_fd'
~/qt-everywhere-opensource-src-4.6.2/libundefined reference to `ts_config'
~/qt-everywhere-opensource-src-4.6.2/lib: undefined reference to `ts_close'
Solution: This is a bug in Qt 4.6. The current workaround is mentioned in step 3. Click here for the bug report and more details.

Qt SDK

1. Install required packages on host:

$ sudo apt-get install libglib2.0-dev libSM-dev libxrender-dev libfontconfig1-dev libxext-dev

2. Download the Qt SDK for Linux installation file from here

3. Make the file executable and run as follows. When prompted for the Installation Directory, store as ~/development/qtsdk-2010.02. Select both Qt Creator and Qt Development Libraries for the install.

$ chmod +x ~/qt-sdk-linux-x86-opensource-2010.02.bin
$ ~/qt-sdk-linux-x86-opensource-2010.02.bin

4. After the installation is complete, in Ubuntu you can launch Qt Creator from Applications --> Programming --> Qt Creator

Build MIDAS

This section will guide you through obtaining the MIDAS source code and building the application executables

1. Create an output directory for MIDAS executables in the NFS directory, ~/workdir/filesys, as:

host $ mkdir ~/workdir/filesys/opt/midas

2. Create a project directory for MIDAS source code

host $ mkdir ~/midas

3. Download the IQ Math Libary from here. Note that at this point, the IQ Math installer is only available in '.exe' form, so you'll need to run the setup.exe on a Windows Machine and copy the 'IQMath_v213' into ~ on your Linux host.

4. Install Subversion (optional)

host $ sudo apt-get install subversion
  • If you are behind a proxy, edit ~/.subversion/servers to configure subversion with your network proxy settings. In the section [global], add the lines below, where <proxy url> and <proxy port> are specific to your network:
http-proxy-host = <proxy url>
http-proxy-port = <proxy port>

5. Download the MIDAS source code onto your linux host. There are two ways you can do this. We would recommend option 2 if you plan to contribute back to the MIDAS project.

  • Option 1: Tar package
You can download the entire tar package, midas.tar.gz from here and store it in ~. Untar the package as:
host $ tar -zxvf midas.tar.gz
  • Option 2: Subversion
Please make sure you have completed 'Initial Setup: Step 4', if you plan to take this route. If you are member of the project, <un> is your GForge username else it is 'anonymous.' Password is your GForge password if you are a member, else just hit Enter when prompted for a password.
host $ cd ~/midas
host $ svn checkout --username <un> https://gforge.ti.com/svn/med_ultrasound/trunk/midas .

6. Set Paths appropriate to your environment:

  • Modify ~/midas/Paths.mak to set paths. Note that <DVSDK> and <IQMATH_DIR> are relative to <DEVHOME>. Here's 'Paths.mak':
DEVHOME = /home/<username>
EXEC_INSTALL_DIR = /home/<username>/workdir/filesys/opt/midas
LINUX_INSTALL_DIR = /home/<username>/workdir/opt/linux-##.##.##.##
DVSDK = dvsdk_3_00_02_44
IQMATH_DIR = IQmath_v213
  • In ~/midas/ultrasound/servers/serverconfig.bld set the C64P.rootDir variable to point to the C6000 code generation tools as:
C64P.rootDir = "/home/<username>/dvsdk_3_00_02_44/cg6x_6_0_16";
  • In ~/midas/ultrasound/apps/appconfig.bld set the GCArmv5T.rootDir variable to point to the Code Sourcery toolchain as:
GCArmv5T.rootDir = "/home/<username>/toolchain/arm-2009q1";

7. Copy initscripts to NFS. Do make backup copies of your original file in case you need to use them later. The ~/midas/ultrasound/initscripts folder contains the following:

  • profile: We've added the necessary environment variables here for tslib and Qt, so that the OMAP picks these up during initialization. Copy this file as follows:
host $ cp ~/midas/ultrasound/initscripts/profile ~/workdir/filesys/etc/profile
  • fstab: We've modified this file to increase the size for tmpfs (DDR) to 128MB. This allows us to copy the input raw data files to DDR during application initialization. Copy this file as follows:
host $ cp ~/midas/ultrasound/initscripts/fstab ~/workdir/filesys/etc/fstab
  • S99omap-demo: We've modified this initialization script to set environment variables and start MIDAS automatically after the EVM has booted up. Copy this file as follows:
host $ cp ~/midas/ultrasound/initscripts/omap-demo ~/workdir/filesys/etc/init.d/omap-demo
  • loadmodules.sh: The MIDAS application uses this shell script to load the CMEM, DSPLink, SDMAK, and LPM modules. In addition, the memory address and size of CMEM pools is defined here based on the size of contiguous buffers the MIDAS application requires. CMEM buffers are used for data that is shared between ARM and DSP. This is data dependent, so if you plan to test your own raw input data with MIDAS you might have to modify these pool sizes. More on this later. Copy this file as follows:
host $ cp ~/midas/ultrasound/initscripts/loadmodules.sh ~/workdir/filesys/opt/midas/.
  • unloadmodules.sh: The MIDAS application uses this shell script to unload the CMEM, DSPLink, SDMAK, and LPM modules. Copy this file as follows:
host $ cp ~/workdir/filesys/opt/dvsdk/omap3530/unloadmodules.sh ~/workdir/filesys/opt/midas/.
  • cmemk.ko, dsplinkk.ko, lpm_omap3530.ko, sdmak.ko: These are the module files for CMEM, DSPLink, SDMAK and LPM. Copy these files as follows:
host $ cp ~/workdir/filesys/opt/dvsdk/omap3530/*.ko ~/workdir/filesys/opt/midas/.

8. The input files (post-RF demod data, parameter files, LUTs) that will be needed by the MIDAS executable are stored in ~/midas/ultrasound/userdata. Copy these files to your <EXEC_INSTALL_DIR> as:

host $ cp -r ~/midas/ultrasound/userdata ~/workdir/filesys/opt/midas/.

9. Create a symbolic link for Qt tools. Since we configure Qt to build with the prefix '/opt/qt' when you build a Qt appliction, it looks for the tools like 'uic' under '/opt/qt' in your host system. Please create a symbolic link here to point '/opt/qt' to '~/workdir/filesys/opt/qt' as:

host $ ln -s ~/workdir/filesys/opt/qt /opt/qt

10. Build the application as follows. This builds the DSP server executable 'mud.x64P' and 'ultrasound' ARM executable.

host $ cd ~/midas
host $ make midas

11. Once the build process is complete, verify that ~/workdir/filesys/opt/midas/. contains the 'mud.x64P' and 'ultrasound' executables

12. You are now ready to run the demo off NFS and start development. For a guide to the user interface see the 'Run MIDAS' section on this page.

Common Gotchas

  • Gotcha 1
Error message:
/opt/qt/bin/uic mainwindow.ui -o ui_mainwindow.h
make[1]: /opt/qt/bin/uic: Command not found
make[1]: *** [ui_mainwindow.h] Error 127
make[1]: Leaving directory `~/midas/ultrasound'
make: *** [all] Error 2
Solution:
The Makefile uses 'uic' to convert the mainwindow.ui file into a header file ui_mainwindow.h. This error occurs when the Makefile cannot find this 'uic' tool. Please ensure that you have performed step 9 above, to create a symbolic link for Qt tools.

SD Card

This section describes how you can obtain a bootable SD card image for MIDAS and write it to an SD card that can be used to run the demo on the OMAP3530 Mistral EVM. You can write the demo SD card on either a Linux or Windows machine.

EVM Settings

Please ensure that your EVM has 256MB DDR before proceeeding. While the EVM is switched off, ensure that the boot mode is set for SD card boot. To do this, set the SW4 DIP switches on the main board as follows:

  • For Micron NAND Flash,
Switch
8
7
6
5
4
3
2
1
State
OFF
OFF
OFF
OFF
OFF
ON
ON
ON




  • For Samsung OneNAND Flash,
 Switch
8
7
6
5
4
3
2
1
State
OFF
OFF
OFF
ON
ON
OFF
OFF
ON




Once the SD card is ready, insert it into the SD card slot on the EVM and power it on. Once the board boots up, the MIDAS demo will start automatically. For a guide to navigate the GUI, see the 'Run MIDAS' section of this page.

Linux

1. Create a directory named 'SDCardImages' on the Linux host where you would like to download the SD card image:

 host $ mkdir SDCardImages

2. To obtain the SD card image, click here to download the file 'midas.tar.gz.' Make sure you place this into the 'SDCardImages' folder


3. Uncompress this file as below. Once this uncompresses, you will see a file 'midas.img' in this directory.

host $ cd SDCardImages
host $ tar –xzf midas_sdcard.tar.gz

4. WARNING! It is important you perform this step very carefully. Not doing so can lead to complete data loss! Plug in your SD card reader with the SD card (min. size of 2GB) and write the image 'midas.img' to the card. Replace <partition> in the command below with sdb, sdc, sdd, etc. depending on where your SD card is mounted.

host $  sudo dd bs=4096 if=midas.img of=/dev/<partition>

5. That’s it, your MIDAS SD card is ready to use! Go to the 'Run MIDAS' section of this for instructions to run the demo

Windows

1. Download and install Cygwin from here

2. Create a new folder with the name SDCardImages in the path C:/cygwin

3. To obtain the SD card image, click here and download the file 'midas_sdcard.tar.gz' into the 'SDCardImages' folder
4. Double click the Cygwin icon on your Desktop to start Cygwin and type the following commands:

host $ cd /cygdrive/c/cygwin/SDCardImages
host $ tar xzf midas_sdcard.tar.gz

5. WARNING! It is important you perform these steps very carefully. Not doing so can lead to complete data loss!

  • Plug in your SD card reader with the SD card (min. size of 2GB)
  • Click on Start --> Control Panel --> Administrative Tools --> 'Computer Management.' On the left hand side, click on the 'Disk Management' tab.
  • The SD card should show up as “Disk1” or “Disk2”. If it shows up as Disk 1 replace <partition> in the command below with “sdb”; if it shows up as Disk 2 use “sdc” and so on. 
host $ dd bs=4096 if=midas.img of=/dev/<partition> 
  • This process can take quite some time to complete and while the image is copying, the terminal does not show any progress or update information. Once copying is successful, you should see a message similar to:

6. That’s it, your MIDAS SD card is ready to use! Go to the 'Run MIDAS' section for instructions to run the demo

Run MIDAS

1. Though the 'S99omap-demo' init script brings up the MIDAS at startup, if you'd like to run the demo application from the target command prompt run as follows:

target $ cd /opt/midas
target $ ./ultrasound -qws

2. Once the application starts, you will first see an initialization screen as below:

Midas splashscreen 640x480.png









 

Once the application completes initialization, you should see MIDAS's GUI. You can use a stylus or your finger to interact with the touchscreen-based GUI.

3. The layout for the MIDAS GUI is as below. When the application starts, you will see only two tabs 'Home' and 'About.' This is the 'Basic' screen. The 'Advanced' screen includes the 'BPU', 'DPU', 'SCU B', 'SCU C' and 'Data' tabs.

MIDAS Qt Touchscreen GUI

4. Home: This tab window provides users with functionality as described below:

  • Scan Type: This allows users to select between datasets. As released, MIDAS comes with the Carotid Artery dataset and the Heart dataset. A dataset includes:
    1. B-mode input data: Multiple frames of post-RF-demod raw data. Reside at ~/midas/ultrasound/userdata/InputData/.
    2. Color Flow input data: Multiple frames of post-RF-demod raw data. Reside at ~/midas/ultrasound/userdata/InputData/.
    3. Data-specific parameter file: One per dataset, which includes data-specific parameters like #scanlines, #samples, #ensembles, LUT file names, and default module parameter file names. Reside at ~/midas/ultrasound/userdata/.
    4. LUTs: Lookup tables for color mapping B-mode and Color Flow, Arbitration, and B-mode Compression. Reside at ~/midas/ultrasound/userdata/LUTs
    5. Module-specific parameter files: Default parameters for each of the modules i.e. B-mode Processing Unit (BPU), Doppler Processing Unit (DPU) and Scan Converter Unit (SCU) for B-mode and Color Flow. Reside at ~/midas/ultrasound/InputData/.
  • Contrast: Three contrast settings for B-mode, High, Medium and Low are available. Related parameters are defined in the BPU parameter file and are customizable
  • Start/Stop: This enables users to Start and Stop image processing to toggle between 'Imaging' and 'Idle' states. When in 'Imaging' state, the ARM reads the raw input data files and sends it to the DSP to perform BPU, DPU and SCU processing. The output frames are displayed on the LCD screen. The same button allows users to stop imaging.
  • Advanced/Basic: As described earlier, this is used to toggle between the 'Basic' and 'Advanced' screens.
  • Bmode/Color: This allows users to toggle between B-mode and Color. When imaging is started, both B-mode and Color Flow data is processed and displayed on the screen.
  • Freeze/Unfreeze: When the user touches this button to 'Freeze' it pauses image processing, and a still frame is continuously displayed on the LCD. The user can now touch this button to 'Unfreeze' and continue image processing.
  • Hide: When in 'Imaging' state, output frames are displayed on the LCD along with the user interface overlayed as translucent. The user can use this 'Hide' button to hide the user interface to have a closer look at the processed output frames.
  • DSP and ARM Load: Displays the DSP and ARM CPU loading in real-time
  • OSD: When in 'Imaging' state, this button allows users to toggle the user interface transparency between translucent and fully opaque (no processed frames can be seen). This allows users the choice to have a closer look at the user interface in 'Imaging' state.

5. About: This tab window displays a summary of the MIDAS demo

6. BPU/DPU/SCU B/SCU C: These tab windows appear in the 'Advanced' screen, and allow users to modify processing parameters for these DSP modules and select LUTs. The textboxes in each of these tabs is populated with default values based on the dataset selected and the parameters defined in the module-specific parameter text files. Users can also change these processing parameters in real-time, in 'Imaging' or 'Idle' state, using the UI. Please note that at this point there is no error checking code for user inputs in the parameter text-boxes, so it is recommended that you play with these parameters only once you are familiar with the specific modules. Documentation is available for each of these modules with the Embedded Processor Software Toolkit for Medical Imaging Applications v2.0, which is free and can be requested here.

7. Data: This tab allows users to modify data-specific parameters in 'Idle' state or view these parameters in 'Imaging' state. Typically these parameters are specific to a dataset and are provided in the default data-specific parameter files.

Common Gotchas

  • Gotcha 1
Issue:
Touchscreen doesn't work! I can see the menu, but it doesn't respond to touch events.
Solution:
1. Let's first rule out a hardware or touchscreen library issue. On your omap's console please type the following to run the touchscreen calibration:
target $ ts_calibrate

You will now see “x=640, y=480” on the console, and should also see a crosshair on the EVM LCD screen. You should touch the crosshair and as you do that, it will move the crosshair to different places, where you need to touch the screen at that spot, so that the touchscreen gets calibrated. After 5 of these cross-hairs, the program will execute.

2. Next, let's run the touchscreen test. On the omap console, please type the command below. Here you can either 'drag' or 'draw' to test the touchscreen

target $ ts_test

3. Next, let's make sure that the process 'cat /dev/zero > /dev/null' is running on your omap target in the background when you try to run midas. This process is required so that we ensure that the CPU does not go into idle state during midas execution. Typically, this should be started in the initscript 'omap-demo' but in case it's not running, we'll need to restart it. On your target type 'ps' to verify this, and if the process doesn't exist please execute the following command before attempting to execute the 'ultrasound' application.

target $ cat /dev/zero > /dev/null
  • Gotcha 2
Issue:
The LCD screen goes dark after a few seconds and comes back up when I hit enter on the console or press a button on the EVM, and this repeats.
Solution:
To save power, the EVM's LCD is set to timeout after a few seconds of idle time. To avoid timeout, we can increase this timeout interval, so that it stays on longer. Let's set this as follows:
target $ echo 600000 > /sys/devices/platform/omapfb/sleep_timeout


Using Custom Input Datasets

As the Software Design block diagram above indicates, MIDAS incorporates the ultrasound signal processing blocks after the data has undergone IQ demodulation. If you are unfamiliar with the ultrasound signal chain, click here for a whitepaper that provides an overview of signal processing blocks in a typical ultrasound system. 

MIDAS includes two input datasets, a sector scan of the heart and a linear scan of the carotid artery. In this discussion, we use the carotid artery data as an example to outline the steps you would need to follow to incorporate your dataset within MIDAS.

Each dataset, includes binary IQ data for a given ultrasound scan, and configuration text files and look-up tables specific to the given input data. The dataset is identified by a data configuration file that defines data parameters like the number of scanlines, samples and ensembles as well as the look-up tables and module parameter files that are specific to the given dataset. The data configuration file resides at 'midas/ultrasound/userdata/.' For the carotid artery dataset, this is 'TI_Carotid.txt.' Please ensure that you follow the guidelines in the table below when you start to incorporate your own dataset.

Parameter Value Guidelines
Label Carotid Label that will be displayed in 'Scan Type' combo box on UI
BmodeData TI_Carotid_tissdata_512x256x69.bin Bmode IQ demodulated data 32bit/sample,16bit I &16bit Q interleaved
BScanLines 256 Should be multiple of 16
BSamples 512 Should be multiple of 32
DopplerData TI_Carotid_velturbdata_48x256x10x69.bin Color IQ demodulated data 32bits/sample,16bit I &16bit Q interleaved
DScanLines 48 Should be multiple of 16
DSamples 256 Should be multiple of 32
DEnsembles 10 Should be multiple of 2
WallFilterCoeffFile TI_Carotid_WallFilterCoeffx10.bin Wall filter coeff, 16bits/sample, Q15, DEnsembles*DEnsembles in size
TissueTableFile TISSUETable_TI_Carotid.txt Bmode Color Mapping LUT 256 samples, 32bits/sample
FlowTableFile FLOWTable_TI_Carotid.txt Doppler Color Mapping LUT 4096 samples, 32bits/sample
ArbTableFile ARBTable_TI_Carotid.txt Arbitration LUT 4096 samples. 8bit/sample
CompressionTableFile COMPRESSTable_TI_Carotid.txt Compression LUT 256 samples, 16bits/sample
BPUParamFile TI_Carotid_bpuparam.txt Configuration parameters for BPU module
DPUParamFile TI_Carotid_dpuparam.txt Configuration parameters for DPU module
SCUBParamFile TI_Carotid_scubparam.txt Configuration parameters for SCU module (Bmode)
SCUCParamFile TI_Carotid_scucparam.txt Configuration parameters for SCU module (Color)


If you look at the loadmodules.sh script in ~/workdir/filesys/opt/midas/ on your filesystem, you will see that the CMEM memory area is defined to be 16MB in size (from 0x86300000 to 0x87300000). The CMEM pool sizes are defined by the command:

“insmod cmemk.ko phys_start=0x86300000 phys_end=0x87300000 pools=3x4096,2x131072,8x524288,4x131072,6x32768,2x16384,4x614400,4x307200 allowOverlap=1”

You will see that the number of pools defined here correspond to the data sizes as shown below for the Carotid data. Note that the number of “bytes/sample” is as required by the DSP modules in use. The “pipe-size” is as defined in DISPLAY_PIPE_SIZE, PROCESS_PIPE_SIZE, both of which are defined as 4 in our current implementation.

So basically, once you know the following 5 data-specific numbers, you can calculate the CMEM pool sizes for your dataset:
BScanlines, BSamples, DScanlines, DSamples, DEnsembles.

Let's go through an analysis for the Carotid Dataset as an example:

Inputs
Bmode Input (post RF demod): 256 scanlines x 512 samples/scanline x 4 bytes/sample = 524288 bytes x 4 pipe-size

Color Input (post RF demo): 64 scanlines x 256 samples/scanline x 8 ensembles x 4 bytes/sample = 524288 bytes x 4 pipe-size

Intermediate Outputs
BPUOut: 256 scanlines x 512 samples/scanline x 1 byte/sample = 131072 bytes x 4 pipe-size

DPUOut: 64 scanlines x 256 samples/scanline x 2 bytes/sample = 32768 bytes x 4 pipe-size

DPUTemp16: 64 scanlines x 256 samples/scanline x 2 bytes/sample = 32768 x 2 pipe-size

DPUTemp8: 64 scanlines x 256 samples/scanline x 1 byte/sample = 16384 bytes x 2 pipe-size

Outputs

SCUOut16: 640 pixels x 480 pixels x 2bytes/sample = 614400 x 4 pipe-size

SCUOut8: 640 pixels x 480 pixels x 1byte/sample = 307200 x 4 pipe-size

In addition you will notice that, there are 3 pools of 4096 and 2 pools of 131072 that aren’t accounted for in the calculation above. These are code-specific used for allocation of some configuration structures, so you can retain those as the same (i.e. pools = 3x4096, 2x131072, and then whatever you calculate).

Please remember to follow the multiplicity requirements described in the table above. Another thing to keep in mind is that CMEM total size defined above is 16MB (0x86300000 to 0x87300000). Please either ensure that when you add all the pool sizes your total CMEM size is under 16MB, or modify the memory map as per guidelines here.


Software Design

This section discusses the software architecture for MIDAS and showcases how easy it is to setup ARM-DSP communication by leveraging TI's Codec Engine, DMAI and IUNIVERSAL APIs.

Overview

The figure below summarizes the MIDAS software implementation. OMAP3530’s C64x+ DSP core runs the DSP/BIOS real-time operating system and handles algorithm processing. There are three main algorithm modules managed by the DSP:

  1. B-mode Processing Unit (BPU): Envelope Detection and Compression
  2. Doppler Processing Unit (DPU): Ensemble Aggregation and Velocity Estimation
  3. Scan Conversion Unit (SCU): Scan converts both B-mode and Color Flow data from the acquired polar/cartesian co-ordinates to the display cartesian co-ordinates

The BPU, DPU, and SCU algorithm modules are packaged into a 'DSP Algorithm Server' that is managed by the Codec Engine and executed on the DSP core. On the ARM, the demo application uses Codec Engine APIs to make a remote procedure call to this 'Algorithm Server'.

The ARM core runs the Linux operating system, and all peripherals are controlled through Linux device drivers, which are part of the PSP. The MIDAS application resides on this Linux filesystem and uses a multithreaded framework to achieve data FIFO management, and to manage the Qt GUI on the On Screen Display (OSD) and service user interrupts via touchscreen events.

MIDAS Software Framework

ARM Application Implementation

The ARM application consists of a main function, and four execution threads running in parallel viz. acquisition, process, display and control threads. All four threads are configured as preemptive/ priority-based scheduled. The display thread has the highest priority, while acquire, process and control are of an equal, lower priority.

  • The main function draws the Qt GUI and then becomes the event handling loop
  • The acquire thread reads the raw input ultrasound data (post IQ demod) into the system
  • The process thread engages the C64x+ DSP for b-mode processing, doppler processing and scan conversion
  • The display thread transfers ultrasound image frames to the frame buffer of the FBDev display driver
  • The control thread computes the ARM and DSP load and services touchscreen events for unhiding the GUI

Fixed-size buffers are exchanged between acquisition-process, and process-display threads for data movement. The thread where the data originates creates and maintains the set of data buffers, and FIFOs are used to put and get buffer pointers, as shown in the figure below. Each data path consists of a set of two FIFOs, one in the direction of data flow, and the other in the reverse direction. The use of the reverse FIFO is to acknowledge that the receiving thread has consumed the data buffer. For example, once the process thread consumes the buffer it receives from the acquire thread on the Acq2ProcFifo it puts the consumed buffer on the Proc2AcqFifo. Only once the acquire thread receives this acknowledgment on Proc2AcqFifo does it free up that buffer and fill up another input buffer on Acq2ProcFifo. In the following subsections we will delve into each of the threads discussed above. It might be useful to follow along with the source code as we discuss these elements.

Main Function

The main function in main.cpp performs necessary initialization tasks, which include loading CMEM, DSPLink, SDMA and LPM modules and moving input data to DDR, before setting up the user interface. Once the main window is setup, this main thread becomes the event handling loop that makes function callbacks in response to touchscreen events. All trigger events (slots) and their associated callback functions (signals) are defined in mainwindow.cpp.'

The image() function is the most important signal in mainwindow.cpp and is triggered when the user touches the 'Start/Stop' button to begin/end image processing. The image() function is responsible for spawning the acquire, process, display and control threads mentioned above, allocating memory for data FIFOs, setting up the Rendezvous and Pause objects that allow thread synchronization, and populating the BPU, DPU, SCU configuration structures with the default parameter values defined in the module-specific configuration files identified. For more details on how configuration and data files are organized and customizable, see the section on this page titled "Using custom input datasets."

Each thread performs its initialization and signals the Rendezvous object when completed. When all threads have finished initializing, they are unlocked simultaneously and start executing their loops. Thread cleanup also uses the same principle. This ensures that buffers which are shared between threads are not freed in one thread while still being used in another thread.

Acquire Thread

The acquire thread function defined in UsAcquire.c reads raw, post-IQ-demodulated ultrasound data from user-specified input file/s. The intended purpose of a separate thread for data acquisition is to try and recreate a real scenario and show independence between the acquisition and display rates. However, this rate independence is not yet implemented in this demo version, and the display thread currently drives the rate of acquisition.

Before the main function spawns the acquire thread, it creates a UsAcquire object and allocates memory for input buffers using the UsAcquire_create() call. As shown in the code section below, the DMAI API Buftab_create() allocates PROCESS_PIPE_SIZE+1 number of input frame buffers, each of the size of a frame, in contiguous memory (CMEM). All data buffers that are shared between ARM and DSP need to be allocated in physically contiguous blocks of memory, which is what this API call helps achieve.

hAcq->hBPUBufTabIn = BufTab_create(PROCESS_PIPE_SIZE+1, hAcq->frameSize, &bAttrs);
hAcq->hDPUBufTabIn = BufTab_create(PROCESS_PIPE_SIZE+1, hAcq->frameSize2, &bAttrs);

For example, for the Heart dataset included with the demo, the BufTab would be created of (PROCESS_PIPE_SIZE + 1) buffers with each buffer as:

  • BPUBufTabIn: 128 scanlines x 416 depth points x 4 bytes/sample = 208 KB/frame
  • DPUBufTabIn: 64 scanlines x 256 depth points x 8 ensembles x 4 bytes/sample = 512 KB/frame

Next, let's focus on how raw Bmode data is acquired. When the acquire thread UsAcquire_thrFxn starts, it first reads PROCESS_PIPE_SIZE number of input frames from the input file, and lines them up in the Acq2ProcFifo using the DMAI Fifo_put() method. Once the FIFO is primed, acquire releases the process thread using the DMAI Pause object, so that process can start taking input buffers off the Acq2ProcFifo for DSP image processing.

As the code fragment below shows, at this point the acquire thread enters its while(!gblSetQuit()) loop, reads the next frame from the input file into the final buffer space left in the allocated Buftab, and places it on the Acq2ProcFifo. Now acquire waits for process to return a consumed buffer/s on the Proc2AcqFifo as acknowledgment, and only then does it free that/those buffer/s and reuses it/them by populating it/them with the next input frame/s. It uses the DMAI's Fifo_get() blocking call on Proc2AcqFifo to achieve this.

pDst = (unsigned char *)Buffer_getUserPtr( hBuf );
bytesRead = fread( pDst, 1, hAcq->frameSize, hAcq->fid );
 
/* ... */
 
Fifo_put( hAcq->hAcq2ProcFifo, hBuf );
numInFifo = Fifo_getNumEntries( hAcq->hProc2AcqFifo );
if (numInFifo > 0) {
    for (i = 0; i < numInFifo; i++) {
        Fifo_get( hAcq->hProc2AcqFifo, &hBuf );
 
        /* ... */
 
        BufTab_freeBuf( hBuf );
 
        /* ... */
    }
}

The acquire while(!gblSetQuit()) loop continues until the global quit flag is true, which indicates that the user has touched the 'Stop' button and the ARM application needs to exit. When this happens, acquire makes sure that it meets up with the other threads using the Rendezvous_meet() method and deallocates any memory associated with it before it exits. The scenario for acquiring raw Color Flow data is similar.

Process Thread

The process thread function defined in UsProcess.c interacts with the C64x+ and engages the DSP to run the BPU, DPU and SCU processing algorithm modules. Just like we saw with the acquire thread, since the output buffers will be shared between ARM and DSP, the process thread also allocates contiguous memory for these using the DMAI API Buftab_create()There are 8 output buffers allocated in the UsProcess_create() function:

  • 1 buffer for the BPU Output:
hUsProcess->hBPUBufTabOut = BufTab_create(DISPLAY_PIPE_SIZE+1, IBPU_ALLOC_PARAMS.size.numSamples, &bAttrs);

For example, for the Heart dataset included with the demo, the BufTab would be created of (DISPLAY_PIPE_SIZE + 1) buffers with each buffer as:

  • BPUBufTabOut: 128 scanlines x 416 depth points x 1 byte/sample = 52 KB/frame
  • 1 buffer for the lumped velocity/variance DPU Output:
hUsProcess->hDPUBufTabOut = BufTab_create(DISPLAY_PIPE_SIZE+1, (hUsProcess->pDpuConfig->varianceConfig.depthPointsToProcess)*(hUsProcess->pDpuConfig->numScanLines)*sizeof(uint_least16_t), &bAttrs);

For example, for the Heart dataset included with the demo, the BufTab would be created of (DISPLAY_PIPE_SIZE + 1) buffers with each buffer as:

  • DPUBufTabOut: 64 scanlines x 256 depth points x 2 bytes/sample = 32 KB/frame
  • 4 buffers for power, velocity, variance and outflow DPU Outputs:
hUsProcess->hDPUBufTabOut16 = BufTab_create(2, (hUsProcess->pDpuConfig->varianceConfig.depthPointsToProcess)*(hUsProcess->pDpuConfig->numScanLines)*sizeof(uint_least16_t), &bAttrs);

hUsProcess->hDPUBufTabOut8 = BufTab_create(2, (hUsProcess->pDpuConfig->varianceConfig.depthPointsToProcess)*(hUsProcess->pDpuConfig->numScanLines), &bAttrs);

For example, for the Heart dataset included with the demo, the BufTab would be created of (DISPLAY_PIPE_SIZE + 1) buffers with each buffer as:

  • DPUBufTabOut8: 64 scanlines x 256 depth points x 2 bytes/sample = 32 KB/frame
  • DPUBufTabOut8: 64 scanlines x 256 depth points x 1 byte/sample = 16 KB/frame
  • 1 buffer for the intermediary B-mode only Scan-Converted Output
hUsProcess->hSCUBufTabOut8 = BufTab_create(DISPLAY_PIPE_SIZE+1, gfxAttrs.dim.width*gfxAttrs.dim.height, BufferGfx_getBufferAttrs(&gfxAttrs));

For example, for the Heart dataset included with the demo, the BufTab would be created of (DISPLAY_PIPE_SIZE + 1) buffers with each buffer as:

  • SCUBufTabOut8: 640 pixels x 480 pixels x 1 byte/sample = 300 KB/frame
  • 1 buffer for the final Scan-Converted Output
hUsProcess->hSCUBufTabOut16 = BufTab_create(DISPLAY_PIPE_SIZE+1, (sizeof(uint_least16_t))*gfxAttrs.dim.width*gfxAttrs.dim.height, BufferGfx_getBufferAttrs(&gfxAttrs));

For example, for the Heart dataset included with the demo, the BufTab would be created of (DISPLAY_PIPE_SIZE + 1) buffers with each buffer as:

  • SCUBufTabOut16: 640 pixels x 480 pixels x 2 bytes/sample = 600 KB/frame

In addition to allocating these buffers, the UsProcess_create() call resets, loads and starts the DSP Engine, and allocates and initializes the BPU, DPU and SCU algorithm modules on the DSP using the iUniversal API as shown in the code section below. This ARM-DSP interaction is explained in further detail later.

hEng = Engine_open(engineName, NULL, NULL)
hAlg = UNIVERSAL_create(hEng, algName, (IUNIVERSAL_Params*)&ISCU_ALLOC_PARAMS)

When the process thread is spawned, UsProcess_thrFxn begins and starts receiving input buffers from the acquire thread on the Acq2ProcFifo/2 FIFOs. It passes a pointer to this BPU/DPU input buffer and a pointer to the allocated BPU/DPU output buffers to the UsProcess_BPU and UsProcess_DPU functions respectively. The output pointer to BPU is then passed to the UsProcess_scanConvertB() function. The outputs of UsProcess_DPU and UsProcess_scanConvertB() are passed as inputs to the UsProcess_scanConvertColor() function. Once scan conversion is complete, the process thread places the pointer to the final output buffer on the Proc2DispFifo.

To illustrate this let's look at the relevant code section for processing in the B-mode only imaging mode:

Fifo_get( hProc->hAcq2ProcFifo, &hInBuf8 );

hBPUOutBuf = BufTab_getFreeBuf(hProc->hBPUBufTabOut);

pBPUOutData = (unsigned char *)Buffer_getUserPtr( hBPUOutBuf );

UsProcess_BPU( hProc, pAcqData, pBPUOutData );

Fifo_put(hProc->hProc2AcqFifo, hInBuf8);

hSCUOutBuf16 = BufTab_getFreeBuf(hProc->hSCUBufTabOut16);

pDispData = (unsigned char *)Buffer_getUserPtr( hSCUOutBuf16 );

UsProcess_scanConvertB( hProc, pBPUOutData, pDispData, inputFrameSize );

The UsProcess_BPU() and UsProcess_scanConvertB() functions invoke the DSP-side scan conversion methods using the IUNIVERSAL API. The UNIVERSAL_process() call invokes the connected function on the DSP-side.

For example for BPU, this is done as:

outBufDesc.numBufs = 1;
inpBufDesc.numBufs = 1;
inpBufDesc.descs[0].bufSize = (sizeof(cplx_least16_t)*bufSize);
inpBufDesc.descs[0].buf = (XDAS_Int8 *)pInp;

outBufDesc.descs[0].bufSize = bufSize;
outBufDesc.descs[0].buf = (XDAS_Int8 *)pOut;

status = UNIVERSAL_process(hUsProcess->hAlgBPU, &inpBufDesc, &outBufDesc, NULL,inArgs, &outArgs);

Initially, the process thread’s prime loop repeats this series of steps for DISPLAY_PIPE_SIZE number of frame buffers. When the process thread is fully primed it releases the display thread for it to start accepting buffers on the Proc2DispFifo. Once the Display thread is triggered, the acquire, process and display threads continue to run till the user touches the 'Stop' button. Since we have used small datasets for demo purposes the acquire loop rewinds to the first input frame and processing and display threads continue to output frames processed in real-time.

Display Thread

The display thread function defined in UsDisplay.c transfers the final scan-converted ultrasound image frames that it receives on the Proc2DispFifo to the frame buffer of the FBDev display device driver. The Display_create() DMAI method opens the display device driver, and the display thread uses a handle to this to copy output buffers to the driver. When the application main spawns the display thread, it waits until the process thread primes the display pipeline, as discussed earlier. Once primed, the display thread pulls in buffers from the process thread, which in turn pulls in buffers from the acquire thread. Based on the display refresh rate specified by the user, 5 / 10 / 15 / 20 / 30 / 60 fps, the display thread gets a new frame from the the Proc2DispFifo and returns a consumed buffer on the Disp2ProcFifo. Therefore it is the display refresh rate that actually controls how fast acquire and process threads can provide buffers.

The display thread uses the H/W resizer to copy the output buffer to the display device driver, instead of using a memcpy() to ensure execution efficiency. DMAI’s Framecopy API is used to perform this buffer copy. The code section below is from the display thread and illustrates the execution flow.

Pause_test(hUsDisp->hPausePrime);
 
if (frmNmbr % dispRateFactor == 0) {
    fifoRet = Fifo_get(hUsDisp->hProc2DispFifo, &hSrcBuf);
}
 
Display_get(hUsDisp->hDisp, &hDstBuf)
Framecopy_config(hFc, hSrcBuf, hDstBuf)
Framecopy_execute(hFc, hSrcBuf, hDstBuf)
if (frmNmbr % dispRateFactor == dispRateFactor - 1) {
    Fifo_put(hUsDisp->hDisp2ProcFifo, hSrcBuf
}
 
Display_put(hUsDisp->hDisp, hDstBuf)

Control Thread

The control thread calculates the ARM CPU load and DSP CPU load using the Codec Engine calls to the getArmCpuLoad() and Engine_getCpuLoad() API functions, respectively. The CPU loading is updated every few seconds on the GUI's 'Home' tab and is represented as a percentage.

DSP Module Integration

In this section we outline the steps we took to integrate the BPU, DPU and SCU algorithm modules to plug-and-play with the Codec Engine using IUNIVERSAL. As Codec Engine Application Developers it is possible to use simple API calls to pass data and configuration parameters between the ARM and DSP cores.

The BPU, DPU and SCU modules, as provided in the Medical Imaging Software Toolkit, are both XDAIS compliant. The term XDAIS refers to an algorithm standard that DSP programmers are recommended to follow to ensure that their algorithms easily integrate into the system and can be called through the Codec Engine. The algorithm modules use the IUNIVERSAL APIs, which provide an interface for the ARM to create and interact with XDAIS algorithms that reside on the DSP. You can find many articles on this wiki that guide you through the process of making your codecs XDAIS compliant.

Since the steps for BPU, DPU and SCU modules are similar, we will focus on the SCU module to illustrate how IUNIVERSAL is setup in this scenario.

MIDAS capitalizes on the IUNIVERSAL API's capability to make a remote procedure call from the ARM to the DSP algorithm, without the need to write any system software for the C64x+. To initiate the ARM-DSP communication, the ARM creates a Codec Engine instance with an Engine_open() call that resets, loads and starts the DSP Engine and returns a handle to the same. Using this handle hEng, the UNIVERSAL_create() API creates an SCU algorithm instance using parameters from the ISCU_Params structure that specify the size of memory to allocate on the DSP. It is important to note that the SCU header file at “midas/ultrasound/algos/scu/src/scu.h” is shared between the DSP and the ARM, which makes it possible for the two cores to share the same interpretation of SCU-specific structures and datatypes, even when both are compiled using different compilers. The UNIVERSAL_create() call returns a handle to the IUNIVERSAL algorithm instance hAlg as:

hAlg = UNIVERSAL_create(hEng, algName, (IUNIVERSAL_Params*)&ISCU_ALLOC_PARAMS);

Next, the ARM populates the SCU configuration structure, scuConfig_t, with parameter values from the user-provided configuration file/s, and with the tissue and flow color mapping lookup tables specified in "midas/ultrasound/userdata/LUTs". The SCU configuration structure is also defined in the SCU header file that ARM and DSP share. To send this configuration information to the DSP, the ARM passes a pointer to the scuConfig_t structure and the handle to the SCU algorithm instance hAlg, using the UNIVERSAL_control() API as shown in the code section below. UNIVERSAL_control() calls the corresponding SCU configuration function SCU_TI_control() on the DSP.

universalStatus.data.numBufs = 1;
universalStatus.data.descs[0].bufSize = sizeof(scuConfig_t);
universalStatus.data.descs[0].buf =(XDAS_Int8 *)(hUsProcess->pScuConfigB);
status = UNIVERSAL_control(hUsProcess->hAlg, XDM_SETPARAMS, &universalDynParams, &universalStatus);

It is important to note here that any buffers that the ARM shares with the DSP, it allocates in contiguous memory. This is necessary because unlike the ARM, the DSP does not have a virtual memory manager and therefore assumes that the buffer is aligned to a 64-bit boundary and is contiguous. In this demo implementation, the allocated buffers reside in CMEM, a contiguous memory manager by TI. When the ARM allocates contiguous memory using the Memory_contigAlloc() or BufTab_create() DMAI API, a CMEM pool that fits the buffer size requested is reserved for this buffer. The number and total size of CMEM pools is defined in the 'loadmodules.sh' script (from /opt/midas/. on the target), which the ARM application runs during initialization. Since both ARM and DSP have access to this CMEM memory space, they only need to exchange buffer pointers.

Once the SCU algorithm instance on the DSP is configured with the parameters it requires, it is ready to begin scan conversion processing. The ARM uses the UNIVERSAL_process() API to call the DSP-side SCU_TI_process() function; based on the scan conversion mode in the configuration, the DSP runs the corresponding processing function. Again, all input and output buffer pointers that the ARM and DSP exchange point to buffers allocated in CMEM.

status = UNIVERSAL_process(hUsProcess->hAlg, &inpBufDesc, &outBufDesc, NULL, &inArgs, &outArgs);

Finally, when the user stops imaging, the UNIVERSAL_delete() API deletes the SCU algorithm instance hAlg. This deallocates all the dynamic memory that was associated with the hAlg instance. The algorithm instance deletion is accompanied with an Engine_close() call which deletes the Codec Engine instance created for ARM-DSP interaction as:

UNIVERSAL_delete(hUsProcess->hAlg);
Engine_close(hUsProcess->hEng);

To summarize, the Codec Engine and IUNIVERSAL APIs allow application developers to seamlessly plug in XDAIS-compliant algorithm modules into their ARM application.

Useful References

For more information on ARM and DSP application development and application notes, white papers and information on TI’s latest offerings in the Medical Imaging space, the following are useful references.

Medical Imaging

ARM and DSP development


Disclaimer

System and equipment manufacturers and designers are responsible to ensure that their systems (and any TI devices incorporated in their systems) meet all applicable safety, regulatory and system-level performance requirements. All application-related information on this website (including application descriptions, suggested TI devices and other materials) is provided for reference only. This information is subject to customer confirmation, and TI disclaims all liability for system designs and for any applications assistance provided by TI. Use of TI devices in life support and/or safety applications is entirely at the buyer's risk, and the buyer agrees to defend, indemnify and hold harmless TI from any and all damages, claims, suits or expense resulting from such use.

All software is licensed under BSD with the following terms and conditions:

Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of Texas Instruments Incorporated nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.