Interactive Tutorial

xSOFTip Explorer Tutorial

Introduction

Welcome to the XMOS xSOFTip Explorer, which lets you browse the XMOS xSOFTip components and select them for use in your system.

This tutorial shows you how to:

  • Create an application in the xSOFTip Explorer
  • Identify the system resources required by the application
  • Change a configuration
  • Identify a suitable xCORE device for your application

Developing applications with xSOFTip Explorer

This tutorial shows you how to use the explorer to select xSOFTip for your first XMOS multicore microcontroller project.

The xSOFTip Explorer interface

The xSOFTip Explorer has five windows:

/files/images/15927/9/explorer.png

  • xSOFTip Browser: shows the xSOFTip components you can chose for your project
  • My System Configuration: the xSOFTip components you have selected
  • My System Information: the resources used by the xSOFTip components you have selected, and the XMOS multicore microcontrollers which suit your application
  • Next Steps: information and activities we recommend you try next
  • Developer Column: online documentation about the xSOFTip, tools and xCORE multicore microcontrollers. The Developer Column can have multiple tabs. While running this tutorial, you can swap to the Main tab to view the documentation for the xSOFTip components.

Create your first application

The xSOFTip Browser displays the available xSOFTip components. xSOFTip blocks use xCORE
resources to provide interfacing, DSP, protocols and control functions, allowing you to
concentrate on building your application.

Add xSOFTip to your configuration

Choose some xSOFTip for your first application.

  1. Select the Ethernet/TCP xSOFTip from the Networking>Ethernet category.

    The Developer Column shows information about this component, including a description of what it does, its features and which xKIT development kits are suitable for use with this xSOFTip. You will need to switch to the Main tab at the bottom of the Developer Column to view the xSOFTip documentation. You can switch back to this tutorial by clicking the xTutorial tab.

  2. Drag the Ethernet/TCP xSOFTip into the My System Configuration window.
  3. Click on the arrow on the left of the Ethernet/TCP module to view the configuration for the component./files/images/15927/9/options.png
  4. Select the Ethernet/TCP Module in the My System Configuration window. The Main tab of the Developer Column provides a description of each configuration option.

As all peripherals in XMOS are implemented using software, you have complete freedom to customise the interface to meet your exact requirements. In this example you will be using the default configuration, so no changes are needed.

Identifying required system resources

The My System Information window shows the resources used by your configuration. As you add components, the window is updated to show the resources used by all the selected xSOFTip components.

/files/images/15927/9/resources.png

Resources include:

  • Logical Cores: 32bit microcontroller cores. XMOS multicore microcontrollers include 4, 6, 8, 10, 12, 16 and 32 core devices.
  • Ports: I/O pins of XMOS multicore microcontrollers are connected to ports, which allow your software to send and receive data to the pins with extremely low latency. Ports are available in different widths: a 1-bit port is connected to 1 I/O pins, a 4-bit port is connected to 4 I/O pins.
  • Clock Blocks: Clock blocks are used to precisely control timing of I/O pins.
  • Chanends: Channel Ends are part of the xConnect system, allowing the cores to send messages to each other through low latency xConnect channels.
  • Timers: Timers are used by the software to control the time at which things happen. They run at 100MHz, giving 10ns precision.

Add more interfaces to your system

All XMOS I/O pins are general purpose, so you can use them for any hardware interface you need.

Select the following xSOFTip components to provide the interfaces you need in our system. Drag them into the My System Configuration window:

  1. Select the I2S Master Audio Driver in the Audio/Interfaces category (or use the Search box to find it).

    This will be used to send and receive audio data.

  2. Select the SDRAM Memory Controller interface in the Memory/Volatile category.

    An external SDRAM will provide a large external memory that you can use to store audio data for the Reverb effect.

  3. Select the Long Reverb Function Library in the Audio/DSP category.

    Use the Reverb library to implement reverb.

  4. Select the I2C Master (Single Bit Ports) Function Library in the Peripherals/Serial category.

    The I2C interface will be used to control external devices.

Changing your configuration

Making changes to your XMOS design is very easy. Perhaps you now need a UART interface rather than I2C.

  1. Right-click on the I2C Master (Single Bit Ports) Function Library and click Delete.
  2. Select the Generic UART Transmitter (Peripherals/Serial) in the xSOFTip Browser and drag it into the My System Configuration window.

Take a look at the other xSOFTip components and add anything else you want in your system.

XMOS and our partners are working on new xSOFTip components all the time. Some components you see here are roadmap components which are in our development plan. If there is a component you require for your system, please let us know – we’d love to hear from you.

Identifying suitable xCORE devices

When you’ve configured your application you can use the My System Information window to identify the xCORE device that most closely match your system requirements.

A list of Possible Devices is displayed at the bottom of the System Information window.

/files/images/15927/9/devices.png

The Next Steps window contains links the XMOS website where you can find further information about the devices.

/files/images/15927/9/next-steps.png

Remember that you will need some resources for your application code. You might want a single core and some memory for control code. The Reverb function library has already been included, but you might want a extra cores and additional memory for your own custom DSP processing.

Next Steps

Congratulations, you’ve now assembled your first xSOFTip components and are ready to start developing your xCORE embedded application.

xSOFTip is all delivered as C code, so you easily change it to meet your exact requirements. You can also take existing C functions and run them on an xCORE. For interfacing to I/O pins and for communicating between cores we’ve added some operations to C, called XC.

Now you have explored our xSOFTip, upgrade to our xTIMEcomposer Studio development suite – free of charge! The xTIMEcomposer Studio is our full development suite, delivering all the code for the xSOFTip components, together with compiler, debugger and everything else you need to build and run your application.

Download xTIMEcomposer Studio
now!

Follow the xTIMEcomposer Studio Tutorial – Simulator, which shows you how to create a PWM project from xSOFTip and simulate it in xTIMEcomposer Studio. See Help > Tutorials > xTIMEcomposer Studio Tutorial in xTIMEcomposer Studio.

xSOFTip Explorer Tutorial Read More »

xTIMEcomposer Studio Tutorial

Introduction

Welcome to xTIMEcomposer. This tutorial provides an introduction to xTIMEcomposer Studio and xSOFTip Explorer. It shows you how to:

  • Create an application using xSOFTip Explorer
  • Create a project from xSOFTip using xTIMEcomposer
  • Edit a xSOFTip component in xTIMEcomposer
  • Test your program in the XMOS simulator

We recommend that you follow the tutorial step-by-step. If you need to download the source code for the examples discussed it is available from xmos.com xTIMEcomposer Tutorial Code Examples
.

The xSOFTip Explorer Perspective

xSOFTip components use xCORE resources to provide interfacing, DSP, protocols and control functions, allowing you to
concentrate on building your application.

  1. Select Window > Open Perspective > XMOS xSOFTip Explorer to open the xSOFTip Explorer perspective.

The xSOFTip Explorer Perspective allows you to browse the XMOS xSOFTip components and select them for use in your system. It provides resource information so you know which XMOS multicore microcontroller is most suitable for your application.

The xSOFTip Explorer Perspective has four windows:

  • xSOFTip Browser – shows the xSOFTip components you can chose for your project
  • My System Configuration: the xSOFTip components you have selected
  • System Information: the resources used by the xSOFTip components you have selected, and the XMOS multicore microcontrollers which suit your application
  • Developer Column: online documentation about the xSOFTip, tools and xCORE multicore microcontrollers

NOTE: Additional documentation will be loaded in other tabs in the Developer Column. Tabs are located at
the bottom of the Developer Column. You will need to switch between the xTutorial tab and the Main tab
to see the xSOFTip documentation.

xSOFTip Component Scope

Each xSOFTip component is categorized with a Scope, which shows the status of the xSOFTip component:

  • General Use: The xSOFTip consists of a complete release from XMOS.

    Complete resource information is available.

    NOTE: All attempts have been made to ensure the correct functionality of this block, but the
    final quality of any product using this block is the responsibility of the user.

  • Early Development: The xSOFTip is suitable for use in development of products and is fully functional.
    However, the maturity of the software is such that extra care must be taken in verifying a product
    using this software block. Resource information is available.
  • Experimental: The xSOFTip is at an experimental/prototype stage. Code exists but is not feature complete.
    Resource information may be available.
  • Roadmap: The xSOFTip is on the XMOS development roadmap. Estimated resource information exists
    for this xSOFTip, but no code is available.
  • Open Source Community: The xSOFTip has been developed by the Open Source community.
    Resource information may not be available.

Your first application

The xSOFTip Browser displays all available xSOFTip components including hardware interfaces,
control functions and DSP processing. This section shows how to use xSOFTip to
implement a precise PWM driver that uses the real-time capabilities of xCORE.

Add the PWM to your application

You can add xSOFTip components directly to your application using the xSOFTip Explorer perspective.

  1. Click on the Tutorial Example LED PWM Driver xSOFTip component in the sliceKIT/demos category.

    The Developer Column shows information on the component including a description of what it does,
    its features and which xKIT Development Kits are suitable for use with this xSOFTip.

  2. Drag the Tutorial Example LED PWM Driver xSOFTip into the My System Configuration window.

    All peripherals in XMOS are implemented using software, giving you complete freedom to
    customize the interface to meet your exact requirements.

    xSOFTip is all delivered as C code, so you easily change it to meet your exact requirements.
    You can also take existing C functions and run them on an xCORE. For interfacing to I/O pins
    and for communicating between logical cores, XMOS has added a handful of operations to C, called ‘XC’.

  3. The My System Information window is updated to show the resources used by your system configuration./files/images/15944/13/resources.png

    Resources include:

    • Logical Cores: 32bit microcontroller cores. XMOS multicore microcontrollers include 4, 6, 8, 10, 12, 16 and 32 core devices.
    • Ports: I/O pins of XMOS multicore microcontrollers are connected to ports, which allow your software to send and receive data to the pins with extremely low latency. Ports are available in different widths: a 1-bit port is connected to 1 I/O pins, a 4-bit port is connected to 4 I/O pins.
    • Clock Blocks: Clock blocks are used to precisely control timing of I/O pins.
    • Chanends: Channel Ends are part of the xConnect system, allowing the logical cores to send messages to each other through low latency xConnect channels.
    • Timers: Timers are used by the software to control the time at which things happen. They run at 100MHz, giving 10ns precision.

A list of Possible Devices is displayed at the bottom of the My System Information window. This shows the xCORE multicore microcontrollers that most are suitable for this application.

Creating a project from the xSOFTip

When you create a project from the xSOFTip Explorer perspective in xTIMEcomposer Studio, an example instantiation of your selected xSOFTip is added to a new project.

A main() function is created, to which you can then easily add your application code.

Create a project

  1. Click the Generate Project button (Generate project button) at the top of the My System Configuration window.
  2. Enter a name for your project in the Generate Project window, for example PWM.
  3. Select sliceKIT Core Board (L16) from the Target Hardware list for your project.
  4. Select tile[1] for the PWM xSOFTip in the Specify the tile mappings … control./files/images/15944/13/project-window.png
  5. Click Next.
  6. Select XS1_PORT_4A as the port you want to use to drive the LEDs.

    The GPIO Slice Card has 4 LEDs connected to XS1_PORT_4A.

    /files/images/15944/13/select-port.png

  7. Click Finish.

    xTIMEcomposer Studio generates a project with your selected xSOFTip.

Using xSOFTip in the Edit perspective

xTIMEcomposer Studio changes to the Edit perspective when it creates a project ready for you to edit the code.
You can switch between perspectives at any time using the Window > Open Perspective menu.

This section shows you how to edit the xSOFTip project to create a simple application that varies the PWM duty cycle.

Editing the xSOFTip code

  1. Open the PWM project in the Project Explorer and double-click on main.xc to open it in the Editing window.

    The main() function created by xTIMEcomposer is displayed.

    The par statement is used to instantiate a Core.
    Each function or statement in a par statement is run on a different core.
    In this example, two cores will be specified, one core to run the pwm_controller()
    task and another to run the xSOFTip PWM driver.

    main() has already instantiated your PWM xSOFTip, so it will run on one logical core on Tile 1.
    Now add our own function to run on another core also on Tile 1.

  2. Add the code below into the par statement to instantiate a pwm_contoller logical core.
    on tile[1]: {
      pwm_controller(c_pwm_duty);
    }
    

    Show where pwm_controller statement goes…

    The on tile[1] statement is used to specify which tile the processing cores are on.
    Each tile in an xCORE multicore microcontroller has eight logical cores. In this example you will use cores on tile 1.

  3. Create a new pwm_controller task above main() in main.xc, that will run on your core
    using the following code:

    void pwm_controller(chanend c_pwm)
    {
    }
    

    Show where pwm_controller task goes…

    The task needs a chanend (channel end) so that it can communicate with the PWM Driver.
    Channel ends are part of the xConnect communication system, allowing the logical cores in a multicore
    microcontroller to communicate with each other with low latency.

    xTIMEcomposer has already created a channel for you: chan c_pwm_duty. Each channel has two
    chanends, allowing two logical cores to communicate with each other. The PWM Driver has already been
    given c_pwm_duty as one of its arguments (look in pwm_tutorial_example.xc to see
    the function definition).

    From the PWM xSOFTip documentation:

    The PWM component uses 1 Core, with
    a channel interface to the rest of
    the application.
    The client application sends two
    values over the channel to configure
    the PWM driver:
    1. The PWM period length
    2. The PWN duty cycle length
    All times are measured with the 100MHz
    reference clock. For example, a value
    of 100 is 100 x 10ns = 1us.
    

    In this case we want to configure the PWM with a low time of 5us and high time of 5us.
    Therefore you need to send it a value of 1000 for the period length and 500 for the duty cycle length.

    Data is sent over the channel c_pwm using the the <: XC operator.

  4. Add the following code to your pwm_controller task:
    // send the PWM period length
    c_pwm <: 1000;
    
    // send the PWM duty cycle length
    c_pwm <: 500;
    

    Show where new code goes…

    The application is now complete and ready to be compiled.

Building your project

  1. Select PWM in the Project Explorer.
  2. Click Project > Build Project (Build button)

    The Console shows the results of the compilation, together with any error messages.
    Your Console should show that the build completed correctly.

  3. Check the bin folder in the Project Explorer.

    You now have a binary (PWM.xe) that you can execute.

Test your project in the simulator

xTIMEcomposer Studio includes a simulator which allows you to simulate your application without hardware.
The simulator includes a waveform analyzer, that you can use to view the I/O pin driven by your PWM xSOFTip.

Run your application in the Simulator

  1. Select Run > Run Configurations.
  2. Double-click on the xCORE Application. This creates a new Run Configuration, automatically filling in the required options.
  3. Select Run on: Simulator/files/images/15944/13/run-config.png
  4. Click the Simulator tab.

    You need to enable tracing of the ports so that you can see the I/O pin behavior.

  5. Select Enable Signal Tracing.
  6. Click Add under Tile Trace Options.
  7. Select tile[1] and tick the Ports option./files/images/15944/13/simulator-config.png
  8. Click Run.

    The application runs on the Simulator. A red Stop button appears in the Console toolbar.

  9. Let the program run for about 10 seconds, then click the red Stop button.

    A PWM.vcd file is added to the Project Explorer.

    /files/images/15944/13/proj-explorer-vcd.png

The next section shows how to look at the waveform of your PWM driver.

The xTIMEcomposer Waveform Viewer

xTIMEcomposer Studio contains a waveform viewer that you can use to look at the waveform of the PWM signal.

Using the waveform viewer

  1. Double-click on PWM.vcd in the Project Explorer.

    The Waves window appears in place of the Console.

    The Signals window appears next to the Project Explorer. You can use this to select which ports
    to show in the Waves window.

  2. Browse to the XS1_PORT_4A port (the port you selected for our PWM port), open the folder
    and double-click on tile[1]_XS1_PORT_4A./files/images/15944/13/signals.png
  3. The Waves window shows the value of the pins for our PWM signal. You may need to zoom
    out to view the PWM transitions.

    The PWM driver uses the deterministic, real-time capabilities of xCORE to generate a
    precise PWM signal. The waveform viewer shows a timeline so you can measure the PWM output.

  4. Verify the timing of your PWM interface by measuring the period length and duty cycle length of your PWM signal.

    You can zoom in and out to see the signals with an appropriate timescale. Click on a signal to place a Marker, then move the cursor to view the time difference between the Marker position and the cursor position.

  5. Double-click on a transition in the Waves window when the cursor changes to a pointing finger.

    The output statement that caused the transition is highlighted in the Editor window,
    allowing you to link the transition to the source code.

Thanks to its timing deterministic architecture, xCORE multicore microcontrollers provide
guaranteed response times up to 100x faster than conventional microcontrollers. xTIMEcomposer
Studio includes the XMOS Timing Analyser (XTA), a tool for analyzing your application and
telling you precisely how long your code will take to execute.

Next steps

Congratulations you have now completed this simple xTIMEcomposer tutorial and are ready to
run it on an xCORE multicore microcontroller. We aim to make evaluating and development
with our xCORE multicore microcontrollers as easy as possible by offering a range
of development kits to meet your specific needs.

Buy a development board

Take a look at our xKITS at http://1m2n3b4v.xmos.com/discover/products/xkits
.

If you’re not sure which to choose, we recommend our sliceKIT Starter Kit
.

Try the sliceKIT Development Board tutorial

If you have a sliceKIT starter Kit we recommend that you follow the sliceKIT Development Board Tutorial, which shows you how to run the project you created in this tutorial on a hardware board. It also shows you how to use the real-time features of xCORE multicore microcontrollers to extend the PWM application to create a temperature controlled LED dimmer.

See Help > Tutorials > sliceKIT Development Board Tutorial.

Browse xSOFTip

Take a look at the other xSOFTip components. You can read through the documentation
in Developer Column, and use them in your project.

XMOS and our partners are working on new xSOFTip components all the time. Some
components you’ll see are Roadmap components which are in our development plan.
If there is a component you require for your system that is not available, please
let us know – we’d love to hear from you.

Try some other tutorials

We provide a range of tutorials covering the xTIMEcomposer tools, xSOFTip and
xKIT development boards. See Help > Tutorials for more information.

xTIMEcomposer Studio Tutorial Read More »

XK-1A Development Board Tutorial

Introduction

The XK-1A is a low-cost development board based on the XMOS XS1-L1 device. It
includes a single L1, four LEDs, two press-buttons, SPI flash memory,
two 16-way IDC connectors and an XSYS debugging interface.

This tutorial shows you how to write some simple XC programs that control
and respond to the XK-1A board components. In this tutorial you learn how to:

  • illuminate the LEDs
  • flash the LEDs at a fixed rate
  • flash the LEDs in sequence
  • respond to a button press
  • connect multiple boards together

Illuminate an LED

This part of the tutorial shows you how to illuminate an LED on your XK-1A, using an
XC port and an output statement.

Create a project

Before writing any code, you need a project to store your files in. To create a new project follow these steps:

  1. Choose FileNewXDE Project (button new project) to open the New XDE Project dialog.

    Alternatively, click here to automate steps 1, 3 and 4.

  2. In Project Name, enter a name such as illuminate.
  3. In Target Hardware, select the option XK-1A Development Board.
  4. In Application Sofware, select the option Empty XC File.
  5. Click Finish.

    The XDE creates a new project, then adds an empty source file to the project and opens it in the editor.

    /files/images/14527/20/app.png

Add the application code

The program below illuminates an LED on an XK-1A.

#include <xs1.h>

out port led = XS1_PORT_4F;

int main () {
  led <: 0b0001;
  while (1)
    ;
  return 0;
}

Copy and paste the code into your project, and then choose FileSave (button save) to save your changes to file.

Examine the code

Take a look at the code in the editor. The declaration

out port led = XS1_PORT_4F;

declares an output port named led, which refers to the 4-bit port 4F. On the XK-1A, the I/O pins of
port 4F are connected to the LEDs labeled LED0, LED1, LED2 and LED3.

Show image of port map…

XC input and output statements make it easy to express I/O operations on ports.
The statement

led <: 0b0001;

causes the value specified to the right of <: to be output to the port specified
to its left (led). The port then drives LED0 high and the other LEDs low,
causing LED0 to illuminate yellow and the other LEDs to remain off.

The empty while loop prevents the program from terminating,
which ensures that the LED remains illuminated.

Build and run your project

To build and run your project, follow these steps:

  1. In the Project Explorer, click your project to select it, and then choose the menu option ProjectBuild Project (button build).

    The XDE displays its progress in the Console. When the build is complete,
    the XDE adds the compiled binary file to the subfolder
    bin/Debug.

    /files/images/14527/20/bin.png

  2. Choose RunRun Configurations.
  3. In the Run Configurations dialog, in the left panel, double-click XCore Application.
  4. In the right panel, in Name, enter the name illuminate.
  5. In Project, ensure that your project is displayed. If not,
    click Browse to open the Project Selection dialog, select your project, and then click OK.
  6. In C/C++ Application, click Search Project to open the Program Selection dialog,
    select your application binary, and then click OK.
  7. In Device options, in Run on, select the option hardware, and
    in Target, ensure that the option “XMOS XTAG-2 connected to L1[0]” is
    selected.

    If your hardware is not displayed, ensure that your XK-1A is
    connected to your PC, and then click Refresh list.

  8. Click Run to save your configuration and run it.

    The XDE loads the binary onto your XK-1A, displaying its progress in
    the Console. When the binary is loaded, the Console is cleared.

  9. On your XK-1A, verify that LED0 is illuminated yellow.
  10. In the Console, click the Terminate button (button stop) to
    stop your application running.

Exercise

To complete this part of the tutorial, perform the following steps:

  1. Modify your application so that it illuminates all four LEDs.

    Show a tip…

    Show a sample answer…

  2. Click the Run button (button run) to reload your last Run Configuration.

    The XDE determines that your source code has been updated and re-builds it,
    displaying progress in the Console.

    If your application contains errors, the XDE displays a dialog asking if you want to
    continue launching the application. Click No, locate the first error in the
    Console and double-click it to go to the offending line in the editor. When
    you have fixed all errors, re-run your application.

  3. On your XK-1A, verify that all four LEDs are illuminated, and then click the
    Terminate button (button stop) to stop your application running.

Flash an LED

This part of the tutorial shows you how to flash an LED at a fixed rate, using an XC
timer and an input statement.

Create a new project

  1. Choose FileNewXDE Project (button new project) to open the New XDE Project dialog.
  2. In Project Name, enter a name such as flash.
  3. In Target Hardware, select the option XK-1A Development Board.
  4. In Application Sofware, select the option Empty XC File.
  5. Click Finish.

    The XDE creates a new project, then adds an empty source file to the project and opens it in the editor.

Add the application code

The program below flashes a single LED on an XK-1A.

#include <xs1.h>

#define FLASH_PERIOD 20000000

out port led = XS1_PORT_4F;

int main (void) {
  timer tmr;
  unsigned isOn = 1;
  unsigned t;
  tmr :> t;
  while (1) {
    led <: isOn;
    t += FLASH_PERIOD;
    tmr when timerafter (t) :> void;
    isOn = !isOn;
  }
  return 0;
}

Copy and paste the code into your project, and then choose FileSave (button save) to save your changes to file.

Examine the code

Take a look at the code in the editor. The declaration

timer tmr;

declares a variable named tmr, and allocates an available hardware timer. The
L1 provides 10 timers, which can be used to determine when an event happens, or to
delay execution until a particular time. Each timer contains a 32-bit
counter that is incremented at 100MHz and whose value can be input at any time.

The statement

tmr :> t;

inputs the value of tmr‘s counter into the variable t. Having recorded the
current time, the statement

t += FLASH_PERIOD;

increments this value by the required delay, and the statement

tmr when timerafter(t) :> void;

delays inputting a value until the specified time is reached. The input
value is not needed, which is expressed as an input to void.

Build and run your project

To build and run your project, follow these steps:

  1. In the Project Explorer, click your project to select it, and then choose the menu option ProjectBuild Project (button build).

    The XDE builds your project, displaying its progress in the Console.
    When the build is complete, the XDE adds the compiled binary file to the application subfolder bin/Debug.

  2. Create a new Run Configuration for your application named flash, and run it.

    Show reminder…

  3. On your XK-1A, verify that LED0 is flashing on-off, and then click the
    Terminate button (button stop) to stop your application running.

Switch between projects

The Run button (button run) can be used to switch between projects.
To complete this part of the tutorial, follow these steps:

  1. Click the arrow to the right of the Run button and select the
    Run Configuration named illuminate.
  2. On your XK-1A, verify that all four LEDs are illuminated.
  3. Click the arrow to the right of the Run button and select the
    Run Configuration named flash.
  4. On your XK-1A, verify that LED0 is flashing on-off.
  5. Modify the source of the flashing LED application to change the value of FLASH_PERIOD from
    20000000 to 40000000.
  6. To build and run, just click the Run button.

    The XDE launches the Run Configuration you most recently selected.

  7. On your XK-1A, verify that LED0 is flashing on-off at half the rate it was flashing previously,
    and then click the Terminate button (button stop) to stop your application running.

Exercise

To complete this part of the tutorial, perform the following steps:

  1. Modify your flashing LED application so that both LED0 and LED1 are
    flashed, with LED0 flashed twice as fast as LED1.

    Show a tip…

    Explain the solution in more detail…

    Show a sample answer…

  2. Run your application.
  3. On your XK-1A, verify that the two LEDs are flashing at different speeds, and then click the
    Terminate button (button stop) to stop your application running.

Flash and cycle LEDs at different rates

This part of the tutorial shows you how to flash an LED while cycling it along the
LEDs on your XK-1A.

Create an application

The program below inputs from one of two timers in a loop.

#include <xs1.h>

#define FLASH_PERIOD 10000000
#define CYCLE_PERIOD 50000000

out port led = XS1_PORT_4F;

int main (void) {
  timer tmrF, tmrC;
  unsigned timeF, timeC;

  tmrF :> timeF;
  tmrC :> timeC;
  while (1) {
    select {
      case tmrF when timerafter(timeF) :> void :
        /* add code to respond to timeout */
        break;
      case tmrC when timerafter(timeC) :> void :
        /* add code to respond to timeout */
        break;
    }
  }
  return 0;
}

Before continuing to the next part of this tutorial, create a new project using this code.

Examine the application code

Take a look at the application code in the editor. The first
statement in main inputs the value of the timer tmrF into the
variable timeF, and the second statement inputs the value
of the timer tmrC into the variable timeC.

In the while loop, the select statement waits for the an input on either tmrF or tmrC
to become ready. It then performs the selected input and executes any
code after it up until the keyword break.

If more than one input becomes ready at the same time, only one is executed in
a single iteration of the loop; the other input is selected on the following iteration.

Exercise 1

To complete this part of the tutorial, perform the following tasks:

  1. Modify the application so that:
    • On each input from tmrC, the program changes which LED is flashed, cycling between all four LEDs in sequence.
    • On each input from tmrF, the program changes the state of the current LED bewteen on and off.

    Show a sample answer…

  2. Build your project, create a new Run Configuration, and run it.

    Show reminder…

  3. On your XK-1A, verify that a flashing LED is cycled between the four LEDs, and then click
    the Terminate button (button stop) to stop your project running.

Exercise 2

To complete this part of the tutorial, perform the following tasks:

  1. Add an additional case to the select statement that responds to Button 1 being pressed by
    displaying the string “pressed” on the console, and then waits for Button 1 to be
    released and displays the string “released”.

    Note that Button 1 drives a 1-bit port high. Pressing the button causes it to stop
    driving the port, and releasing results in it driving again.
    You can test for these conditions using the input condition pinseq(0) and pinseq(1).

    Show image of port map…

    To print a message to the console, you can include the standard C library header stdio.h and
    use the the library function printf.

    Explain the solution in more detail…

    Show a sample answer…

  2. Run your application.

    A flashing LED should cycle between the four LEDs on your XK-1A.

  3. Verify that pressing and holding button BUT1 causes the message “pressed”
    be displayed in the Console and the LEDs to stop flashing. Releasing the
    button should cause the message “released” to be displayed
    and the flashing LED should continue cycling.

    Note that you may need to push the button down firmly on your XK-1A to operate it.

  4. In the Console, click the Terminate button (button stop) to
    stop your application running.

Run tasks concurrently

This part of the tutorial shows you how to run tasks concurrently on different threads, using
the XC par statement and channel communication.

Create a project

The program below creates two concurrent threads, which run
two separate tasks independently of each other.

#include <xs1.h>
#include <stdio.h>

#define FLASH_PERIOD 10000000
#define CYCLE_PERIOD 50000000

out port led = XS1_PORT_4F;
in port but1 = XS1_PORT_1K;

void flashLEDs4bitPort(out port led, int flashPeriod, int cyclePeriod) {
  /* Code to flash 4 LEDs connected to a 4-bit port in a cycle */
  unsigned ledOn = 1;
  unsigned ledVal = 1;
  timer tmrF, tmrC;
  unsigned timeF, timeC;

  tmrF :> timeF;
  tmrC :> timeC;

  while (1) {
    select {
      case tmrF when timerafter(timeF) :> void:
        ledOn = !ledOn;
        if (ledOn)
          led <: ledVal;
            else
          led <: 0;
        timeF += FLASH_PERIOD;
        break;
      case tmrC when timerafter(timeC) :> void:
        ledVal <<= 1;
        if (ledVal == 0x10)
          ledVal = 1;
        timeC += CYCLE_PERIOD;
        break;
    }
  }
}

void respondToButton(in port but) {
  /* Code to respond to a button press */
 while (1) {
    but when pinseq(0) :> void;
    printf("Pressed\n");
    but when pinseq(1) :> void;
    printf("Released\n");
  }
}

int main(void) {
  par {
    flashLEDs4bitPort(led, FLASH_PERIOD, CYCLE_PERIOD);
    respondToButton(but1);
  }
  return 0;
}

Before continuing to the next part of this tutorial, create a new project using this code.

Examine the application code

Take a look at the code in the editor. In main, the two statements
inside the braces of the par are run concurrently: the current thread
allocates a new hardware thread; the current thread then runs the function
flashLEDs4bitPort; and the new thread runs the function respondToButton.
The L1 device has a total of eight available hardware threads.

To complete this part of the tutorial, perform the following tasks:

  1. Build your project, create a new Run Configuration, and run it.
  2. Verify that a flashing LED cycles between the four LEDs on your XK-1A.
    Pressing and holding button BUT1 should cause the message “pressed”
    be displayed in the Console and the LEDs should continue to flash.
    Releasing the button should cause the message “released” to be displayed.
  3. In the Console, click the Terminate button (button stop)
    to stop your project running.

Communicate between threads

A channel provides a synchronous, bidirectional link between two threads. It
consists of two channel ends, which two threads can use to interact on demand using
the XC input and output statements.

The program below creates two threads which are connected using a channel, and
communicates a value over this channel.

void snd(chanend cout) {
  cout <: 1;
}

void rcv(chanend cin) {
  int x;
  cin :> x;
}

int main (void) {
  chan c;

  par {
    snd(c);
    rcv(c);
  }

  return 0;
}

The function snd declares a channel end parameter cout, which refers to one
end of a channel. It uses the XC output statement to output the value 1 to a
receiving thread.

The function rcv declares a channel end parameter cin and uses the XC
input statement to input a value to the local variable x.

The function main declares a channel c. The locations of its two channel ends
are established through its use in two statements of the par.

Exercise 1

To complete this part of the tutorial, perform the following tasks:

  1. Modify your application so that after pressing and releasing button BUT1, the flashing LED
    changes direction. This requires the function respondToButton to communicate with
    the function flashLEDs4bitPort.

    Explain the solution in more detail…

    Show a sample answer…

  2. Run your application.

    A flashing LED should cycle between the four LEDs on one of the XK-1As.

  3. On your XK-1A, verify that pressing and releasing button BUT1 causes
    the flashing LED to change direction, and then click the Terminate
    button (button stop) to stop your project running.

Exercise 2

This part of the tutorial shows you how to put ports and threads on different processor cores. It requires you to have two XK-1As available. Follow these steps:

  1. Connect the two XK-1As together./files/images/14527/20/multiple-boards.png
  2. Choose FileNewXDE Source File (button new source file).
  3. In the New XDE Source File dialog, in File Name, enter the name Dual-XK1A.xn.
  4. In Location, ensure that your project folder is selected.
  5. Click Finish.

    The XDE creates an empty source file, adds it to your application and opens it in the editor.

  6. At the bottom left of the editor, click the Source tab, copy the code in the window below into your new source file, and then save it.
    <?xml version="1.0" encoding="UTF-8"?>
    
    <Network xmlns="http://1m2n3b4v.xmos.com"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://1m2n3b4v.xmos.com http://1m2n3b4v.xmos.com">
    
      <Declarations>
        <Declaration>core stdcore[2]</Declaration>
      </Declarations>
    
      <Packages>
        <Package Id="P1" Type="XS1-L1A-TQ128">
          <Nodes>
            <Node Id="Master" Type="XS1-L1A" InPackageId="0"
                  Oscillator="20MHz" SystemFrequency="400MHz">
              <Boot>
                <Source Location="SPI:bootFlash"/>
                <Bootee NodeId="Slave" Core="0"/>
              </Boot>
              <Core Number="0" Reference="stdcore[0]">
                <Port Location="XS1_PORT_1A" Name="PORT_SPI_MISO"/>
                <Port Location="XS1_PORT_1B" Name="PORT_SPI_SS"/>
                <Port Location="XS1_PORT_1C" Name="PORT_SPI_CLK"/>
                <Port Location="XS1_PORT_1D" Name="PORT_SPI_MOSI"/>
                <Port Location="XS1_PORT_4A" Name="PORT_LED"/>
              </Core>
            </Node>
          </Nodes>
        </Package>
    
        <Package Id="P2" Type="XS1-L1A-TQ128">
          <Nodes>
            <Node Id="Slave" Type="XS1-L1A" InPackageId="0"
                  Oscillator="20Mhz" SystemFrequency="400MHz">
              <Boot>
                <Source Location="XMOSLINK"/>
              </Boot>
              <Core Number="0" Reference="stdcore[1]">
                <Port Location="XS1_PORT_1K" Name="PORT_BUTTON"/>
              </Core>
            </Node>
          </Nodes>
        </Package>
      </Packages>
    
      <Links>
        <Link Encoding="2wire" Delays="4,4">
          <LinkEndpoint NodeId="Master" Link="X0LD"/>
          <LinkEndpoint NodeId="Slave" Link="X0LC"/>
        </Link>
      </Links>
    
      <ExternalDevices>
        <Device NodeId="Master" Core="0" Name="bootFlash"
                Class="SPIFlash" Type="AT25FS010">
          <Attribute Name="PORT_SPI_MISO" Value="PORT_SPI_MISO"/>
          <Attribute Name="PORT_SPI_SS"   Value="PORT_SPI_SS"/>
          <Attribute Name="PORT_SPI_CLK"  Value="PORT_SPI_CLK"/>
          <Attribute Name="PORT_SPI_MOSI" Value="PORT_SPI_MOSI"/>
        </Device>
      </ExternalDevices>
    
      <JTAGChain>
        <JTAGDevice NodeId="Master" Position="0"/>
        <JTAGDevice NodeId="Slave" Position="1"/>
      </JTAGChain>
    
    </Network>
    
  7. Make it default target
  8. In the Project Explorer, expand your new file to reveal the header file platform.h, and expand this header to reveal the symbols stdcore[0] and stdcore[1].

    These symbols name the two processor cores for your new target, which can be referred to in your XC application.

  9. In your XC source file, change the header include file <xs1.h> to <platform.h>.
  10. Modify the declaration of the port led to place it on the first core, as shown below:

    on stdcore [0]: out port led = XS1_PORT_4F;

  11. Modify the declaration of the port but1 to place it on the second core.
  12. Modify the call to the function flashLED to place it on the first core, as shown below:

    on stdcore [0]: flashLED(arguments);

  13. Modify the call to the function respondToButton to place it on the second core.
  14. In the Project Explorer, expand your project and application folder, and then double-click on the file Makefile.
  15. In the Makefile editor, in Target, select the option Dual-XK1A, and then choose FileSave (button save) to save your changes to file.
  16. Run your application.

    A flashing LED should cycle between the four LEDs on one of the XK-1As. Pressing and releasing button BUT1 on the other
    XK-1A should cause the direction of the flashing LED to change.

This tutorial provides only a basic introduction the XK-1A hardware.

For more information on the board refer to the XK-1A Hardware Manual
.

For more information on programming in XC see Programming XC on XMOS Devices
.

XK-1A Development Board Tutorial Read More »

xTIMEcomposer Timing Tutorial

Introduction

The XMOS architecture has predictable timing, which allows many
interfaces to be performed in software. This tutorial shows you how to add
timing constraints to the parts of your program that must run within strict
time limits, enabling correct real-time behavior to be validated for your
target device at compile-time.

This tutorial shows you how to:

  • Open a sample UART project in the xTIMEcomposer Studio Timing Perspective
  • Validate the UART transmitter’s timing constraints interactively
  • Validate the UART transmitter’s timing constraints during compilation

Open a project in the Timing Perspective

This part of the tutorial shows you how to create a project in xTIMEcomposer Studio and open it in the Timing view.

Create an application

Before writing any code, you need a project to store your files in.
To create a project follow these steps:

  1. Choose FileNewxTIMEcomposer Project button new project
    to open the New xTIMEcomposer Project dialog.
  2. In Project Name, enter a name such as timing.
  3. In Target Hardware, select the option XK-1A Board.
    You don’t need this board to complete the tutorial.
  4. In Application Software, select the option Empty XC File.
  5. Click Finish.

xTIMEcomposer Studio creates a new project containing an application. It then
adds an empty source file to your application and opens it in the editor.

/files/images/14045/12/app.png

Add the code

The program below implements a UART interface:

/*
 * ==========================================================
 * Name        : uart-loopback.xc
 * Description : UART loopback example
 * ==========================================================
 */

#include <xs1.h>
#include <print.h>
#include <platform.h>

#define NUM_BYTES 3
#define BIT_RATE 115200
#define BIT_TIME XS1_TIMER_HZ / BIT_RATE

void txBytes(out port txd, char bytes[], int numBytes);
void txByte(out port txd, int byte);
void rxBytes(in port rxd, char bytes[], int numBytes);
char rxByte(in port rxd);


out port txd = XS1_PORT_1H;
in port rxd = XS1_PORT_1I;

int main()
{
  char transmit[] = { 0b00110101, 0b10101100, 0b11110001 };
  char receive[] = { 0, 0, 0 };

  // Drive port high (inactive) to begin
  txd <: 1;

  par {
    txBytes(txd, transmit, NUM_BYTES);
    rxBytes(rxd, receive, NUM_BYTES);
  }

  return 0;
}

void txBytes(out port txd, char bytes[], int numBytes)
{
  for (int i = 0; i < numBytes; i += 1) {
    txByte(txd, bytes[i]);
  }
  printstrln("txDone"); // Transmit_Done
}

void txByte(out port txd, int byte)
{
  unsigned time;

  // Output start bit
  txd <: 0 @ time; // Endpoint A

  // Output data bits
  for (int i = 0; i < 8; i++) {
    time += BIT_TIME;
    txd @ time <: >> byte; // Endpoint B
  }

  // Output stop bit
  time += BIT_TIME;
  txd @ time <: 1; // Endpoint C

  // Hold stop bit
  time += BIT_TIME;
  txd @ time <: 1; // Endpoint D
}

void rxBytes(in port rxd, char bytes[], int numBytes)
{
  for (int i = 0; i < numBytes; i += 1) {
    bytes[i] = rxByte(rxd);
  }

  printstrln("rxDone");
  for (int i = 0; i < NUM_BYTES; i++) {
    printhexln(bytes[i]);
  }
}

char rxByte(in port rxd)
{
  unsigned byte, time;

  // Wait for start bit
  rxd when pinseq (0) :> void @ time;
  time += BIT_TIME / 2;

  // Input data bits
  for (int i = 0; i < 8; i++) {
    time += BIT_TIME;
    rxd @ time :> >> byte;
  }

  // Input stop bit
  time += BIT_TIME;
  rxd @ time :> void;

  return (byte >> 24);
}

Copy and paste the code into your project, and then choose FileSave button save to save your changes to file.

Examine the code

Take a look at the code in the editor.

The source code contains a transmitter thread and receiver thread running concurrently.
The UART is configured to operate at a data rate of
115200 bits/s, and the transmitter outputs bytes of data as shown in the diagram below.

/files/images/14045/12/uart-drive.png

The quiescent state of the wire is high.

A byte is sent by first driving a start bit (0), followed by the eight data bits
and finally a stop bit (1). A rate of 115200 bits/s
means that each bit must be driven for a period of 1/115200 = 8.68us.

The transmitter source code has the following structure:

TX ->
TX-BYTE ->
  Output start bit     (Endpoint A)
  Loop-8
    Output data bit    (Endpoint B)
  Output stop bit      (Endpoint C)
  Wait for bit period  (Endpoint D)

When compiled and run on a target device, the time taken to execute
the code between the following pairs of endpoints must always be less than 8.68us:

  • Route 1: From Endpoint A to Endpoint B (from the start bit to data bit)
  • Route 2: From Endpoint B to Endpoint B (between consecutive data bits)
  • Route 3: From Endpoint B to Endpoint C (from the last data bit to the stop bit)
  • Route 4: From Endpoint C to Endpoint D (holding the stop bit for the bit period)
  • Route 5: Between successive calls to the function txByte

If the constraint on the fifth route is not met, individual bytes will be transmitted correctly,
but the UART will operate at a lower data rate.

More generally, a route consists of the set of all paths through which control can flow between
two endpoints. Each route has a worst-case time, in which branches always follow the path that
takes the longest time to execute. This time must satisfy the constraint for the program to be
guaranteed correct under all possible executions.

Validate timing constraints interactively

Before you can time your program, you need to compile it in Release mode. If you compile in Debug mode, the compiler will not optimize your program, and may produce conditional branches that are never executed at run-time. In this case you will need to manually exclude these paths from the timing analysis.

Compile the project in Release mode

  1. Select your project in the Project Explorer.
  2. Click the arrow next to the Build button button build in the main toolbar
    and select Release from the drop-down list.
  3. Select the compiled binary in the Project Explorer in the
    subfolder bin/Release.
  4. Select RunTime AsxCORE Application button time build
    to open your program in the Timing Perspective.

Validate Route 1

To check that the first route meets its timing constraint, follow these steps:

  1. In the editor, locate the first output statement (Endpoint A), right-click
    on the endpoint marker in the left margin to bring up a menu, and choose
    Set from endpoint.

    A green dot icon xta from is displayed in the top-right quarter of the marker.

  2. Locate the second output statement (Endpoint B), right-click on its endpoint
    marker and choose Set to endpoint.

    A red dot is icon xta to displayed in the bottom-right quarter of the marker.

  3. Click the Analyze Endpoints button button xta analyze in the main toolbar.

    The timing analyzer identifies a single path between Endpoints A and B, which it times.
    The analyzed route is added to the Routes list in the top panel of the Routes view.
    The bottom panel of the Routes view shows a textual representation of the structure and
    timing for the selected route. The Structure panel in the Visualizations view shows
    a graphical visualization of the structure and timing for the selected route.

    In the Routes list, a red question mark icon icon xta none is displayed next
    to the route name, indicating that no timing constraint is set.

    /files/images/14045/12/visualisation-none.png

  4. Right-click on the route in the Routes list to bring up a menu and choose
    Set timing requirement to open the Requirement box.
  5. Enter a value of 8.68, select the unit us and click OK.

    The red question mark is replaced by a green tick icon xta pass, indicating that
    the route meets the specified timing constraint.

  6. Hover over the route to view information such as the number of paths and worst-case timing.

Validate Route 2

To check that the second route meets its timing constraint, follow these steps:

  1. Right-click on the marker for Endpoint B to bring up a menu, and choose Set from endpoint.

    The warning triangle generated by the previous Analyze endpoints action obscures the endpoint marker,
    but you can still set a new endpoint by right-clicking on the marker.

    This statement is now set as both the from and to endpoints icon xta from to.

  2. Click Analyze Endpoints button xta analyze.

    The timing analyzer identifies the path around the loop. It also attempts to
    identify paths that exit the loop and then later re-enter it.

    The lower panel of the Routes view shows the timing of the route is marked as unresolved,
    which means that some of the paths do not successfully reach the to endpoint. In this case
    the unresolved path ends at the exit system call and cannot therefore possibly re-enter the
    loop, which means it should be excluded from the analysis.

  3. Right-click on the first endpoint marker after the loop (Endpoint C) and choose Exclude.

    All paths that pass through this marker are excluded, leaving only one
    path around the loop remaining in the route.

  4. In the Routes view, set a timing constraint of 8.68us.

    The status of the route is updated, indicating that the route now meets its timing constraint.

Validate Routes 3 and 4

Use the techniques introduced in the previous two sections to check that Route 3 (Endpoint B to Endpoint C) and Route 4 (Endpoint C to Endpoint D) meet their timing constraints.

Once completed, a total of four routes should be displayed in the Routes list.

Validate Route 5

To check that route 5 meets its timing constraint (between
consecutive byte transmissions), follow these steps:

  1. Set the endpoints for a route from Endpoint D to A.
  2. In the function txBytes, right-click on the endpoint marker
    following the loop and choose Add to exclusion list.

    The timing analyzer will not attempt to analyze paths outside of the loop.

  3. Click Analyze Endpoints button xta analyze in the main toolbar to create the route.
  4. In the Routes view, set a timing constraint of 8.68us.

    The status of the route is updated, indicating that the route meets its timing constraint.

    You should have a total of five routes in the Routes list.

Validate the timing constraints during compilation

Having checked that all of the UART timing constraints are met, you
can create a script that performs the same checks every time the
program is compiled.

Generate a timing script

To create a timing script and update the source file with
the pragmas required to make the script portable, follow these steps:

  1. Click Generate Script button icon xta script in the main toolbar.

    The Script Options dialog opens.

  2. Enter a name for the script (with a .xta extension) in the Script location text box.
  3. To change the names of the pragmas added to the source file,
    click the values in the Pragma name fields and edit.
  4. Click OK to save the script and update your source code.

    xTIMEcomposer Studio adds the script to your project and opens it in the editor.

    /files/images/14045/12/xta.png

The next time you compile your program, the timing constraints are checked and any failures
are reported as compilation errors.

Congratulations you have finished this basic introduction the timing capabilities provided by the tools. For further information on using the XTA see the Timing Analyzer Manual
.

For more information on using the xTIMEcomposer tools see the xTIMEcomposer User Guide
.

xTIMEcomposer Timing Tutorial Read More »

sliceKIT Development Board Tutorial

Introduction

This tutorial provides an introduction to XMOS sliceKIT development boards.

NOTE: Before starting it you should complete the xTIMEcomposer Studio – Simulator Tutorial
, which shows you how to create a PWM project that is used as the starting point of this tutorial.

The tutorial shows you how to:

  • Set up the sliceKIT hardware
  • Run the PWM application on your sliceKIT board
  • Dim the LEDs up and down
  • Add printing
  • Add an I2C interface to read the temperature
  • Next steps

We recommend that you follow the tutorial step-by-step. If you need to download the source code for the examples discussed it is available from xmos.com sliceKIT Development Board Tutorial Code Examples
.

Set up the sliceKIT hardware

To follow this tutorial you need the following sliceKIT board and cards

  • XP-SKC-L2 sliceKIT Core Board
  • XA-SK-GPIO GPIO sliceCARD
  • xTAG-2 Debug Adapter

All these boards are available in the sliceKIT Starter Kit – see www.xmos.com/slicekit
.

Details on mapping between pins on the Core Board and ports is available in
the sliceKIT Core board
documentation.

Connect up the boards

  1. Connect the xTAG Adapter to the XSYS connector on the sliceKIT Core board.
  2. Connect the xTAG-2 to the xTAG adapter.
  3. Connect the GPIO sliceCARD to the sliceKIT Core board using the connector marked with the SQUARE.
    sliceKIT core board with xTAG and GPIO sliceCARD

    /files/images/16044/10/hardware.png

Run the PWM application on your sliceKIT board

In the xTIMEcomposer Studio – Simulator Tutorial
, the PWM application is run on the xCORE simulator on your PC or Mac. You can quickly change the Run Configuration to execute the application on your sliceKIT development board.

Run your application

  1. Open the workspace you used for the xTimeComposer Studio Tutorial – Simulator.
  2. Select Run > Run Configurations.
  3. Change the Device Options from simulator to hardware.

    The Target should be shown as XMOS XTAG-2 connected to L1[0..1].

    Run Configuration

    /files/images/16044/10/run-configuration.png

    xTIMEcomposer uses JTAG to load the application directly into the internal SRAM in the xCORE multicore microcontroller. The SRAM is a fast, single cycle memory and does not include caches, which makes all memory accesses deterministic.

    Loading directly into SRAM is used during development, as it provides the fastest mechanism for loading code into the device. In production, xCORE devices usually boot from SPI flash. xTIMEcomposer includes a flash programming utility that can be used by creating a Flash Configuration in the same way as a Run Configuration – see Run > Flash.

  4. Click Run.

After a short delay during which the sliceKIT Core Board is booted over High Speed USB and JTAG, all four LEDs on the GPIO sliceCARD light up at 50% brightness (50% duty cycle).

Dim the LEDs up and down

In the xTIMEcomposer Studio – Simulator Tutorial
, you created a basic pwm_controller task to initialize the period and duty cycle of the pwm_tutorial_example task via a channel.

The pwm_tutorial_example task also accepts updates to the duty cycle value at any time. You can therefore enhance the pwm_controller task to control the duty cycle of the PWM over time, thereby varying the brightness of the LEDs.

This section shows you how to:

  • Start with LEDs off and increase the brightness gradually to 100%.
  • Once 100% brightness is reached, decrease brightness gradually to 0%.
  • Put this function into a loop so that the LEDs continuously increase and decrease in brightness.

NOTE: The LEDs on the GPIO sliceCARD are active-low, which means a duty cycle of 100% corresponds to LEDs off. Duty cycle of 0 corresponds to LEDs at 100% brightness.

Write a new PWM controller

Before you write any code, check the LED related information in the GPIO sliceCARD Hardware Guide
.

  1. Add the following code to main.xc, which implements a wait function that will be used to delay successive updates of the PWM duty cycle:
    void wait(unsigned wait_cycles) {
      timer tmr;
      unsigned t;
    
      // read the current time
      tmr :> t;
      // event will occur wait_cycles * 10ns in the future
      tmr when timerafter (t+wait_cycles) :> void;
    }
    

    Show where the wait code goes

    The function uses a timer to emit an event at some time in the future. Timers use the 100MHz reference clock to provide programmable delays to the software. The 100MHz clock gives 10ns resolution to these delays, allowing the software to precisely control the time at which actions occur. The logical core remains idle until that event happens, thereby saving power.

  2. Update the current pwm_controller function with the following code:
    void pwm_controller(chanend c_pwm)
    {
      // PWM period is 10us. 1000 cycles at 10ns (100MHz ref clock)
      int period = 1000;
      // duty_cycle starts at 100% which switches all LEDs off
      // (LEDs active low on GPIO sliceCARD)
      int duty_cycle = 1000;
      // duty cycle step (up or down)
      unsigned step = period / 100;
      // duty_cycle delta.
      int delta = -step; // start with increasing brightness
    
      // output the PWM period length to a channel
      c_pwm <: period;
      // output the PWM duty cycle length to a channel
      c_pwm <: duty_cycle;
    
      while(1) {
        // update the duty cycle length
        c_pwm <: duty_cycle;
    
        wait(XS1_TIMER_HZ/100); // 0.01 s
        duty_cycle += delta;
        if(duty_cycle > period) {
          delta = -step; // increase brightness
          duty_cycle = period;
        }
        else if(duty_cycle < 0) {
          delta = step; // decrease brightness
          duty_cycle = 0;
        }
      }
    }
    

    Show where the pwm_controller code goes

    The new pwm_controller updates the PWM logical core, by periodically sending new PWM settings to it over the channel. There is a delay of 10ms between each update to the PWM core. The step between every successive duty cycle is 1/100 of a PWM period which means the time to go from 0 to 100% duty (LED fully on to LED fully off) is 100 * 10ms = 1 second.

  3. Click PWM in the Project Explorer and select Project > Build Project.
  4. Click Run.

    The four LEDs are dimmed up and down every 2 seconds.

You can extend this simple example to any real-time task. Using the precise 100MHz timer, together with the deterministic execution of xCORE multicore microcontrollers makes it easy to accurately control your real-time tasks.

Add printing

The XTAG2 USB-JTAG converter and xTIMEcomposer provide a complete suite of development and debug tools including:

  • debugger including breakpoints, watchpoints and single stepping.
  • XScope real-time, in-circuit instrumentation that lets you view what’s going on in your code at run-time.
  • JTAG I/O, allowing you to print from your application and view the output within xTIMEcomposer.

Using print statements over JTAG

  1. Add the following code at the top of main.xc to include the print library:

    #include "print.h"

  2. Add a welcome message above the first channel communication in the pwm_controller() function using the printstr statement:
    int delta = -step; // start with increasing brightness
    
    printstr("Welcome to the XMOS PWM tutorial");
    
    // output the PWM period length to a channel
    c_pwm <: period;
    

    Show where the printstr code goes

    The print library includes functions for printing various different formats, including strings and integers.

  3. Build and run the application.

    Your welcome message is printed to the xTIMEcomposer Console.

    xTIMEcomposer Console

    /files/images/16044/10/console.png

Add an I2C interface to read the temperature

The GPIO sliceCARD features an ADC that has a linearized thermistor connected for measuring temperature. The ADC is accessed via an I2C interface.

You can add an I2C Master interface using the xSOFTip I2C component to read the temperature, and use an interpolation function to convert the ADC value into a temperature, in the range -10 to 60 degrees C.

This section shows you how to:

  • Read the ADC value periodically using the I2C master interface.
  • Convert the ADC value into a temperature value.
  • Print the temperature value.

Integrate the I2C interface

  1. Find the I2C Master (Single Bit Ports) xSOFTip in the xSOFTip Browser by typing I2C in the Search field. This I2C Master uses 1-bit ports for SCL and SDA.
  2. Find the Makefile inside your project in the Project Explorer.

    As you are adding a module to an existing project, you need to add the module to the Makefile.

  3. Right-click on the Makefile and select Open With > Makefile Editor.
  4. Add module_i2c_master to USED_MODULES.

    Your USED_MODULES will now be:

    USED_MODULES = module_i2c_master module_pwm_tutorial_example

  5. Drag the I2C Master xSOFTip into the Project Explorer to add it to your project.

    module_i2c_master is displayed in the project tree.

  6. Include i2c.h in your main.xc application file.

    #include "i2c.h"

  7. Add the I2C ports to the top section of main.xc:
    on tile[1] : struct r_i2c i2cOne = {
      XS1_PORT_1F,
      XS1_PORT_1B,
      1000
    };
    

    Show where the port code goes

    This declares a structure with two 1-bit ports which are the SCL and SCK pins for I2C. The third member of the structure is the speed of the bus. The I2C Programming Guide gives this description of the structure:

    Structure Members

    port scl – Port on which clock wire is attached. Must be on bit 0

    port sda – Port on which data wire is attached. Must be on bit 0

    unsigned int “clockTicks“ – Number of reference clocks per I2C clock, set to 1000 for 100 kHz.

    The location of the I2C ports can be found in the GPIO sliceCARD documentation.

Examine the I2C Master interface

The module supports multiple I2C Masters but you only need one Master (the xCORE) in this application.

The I2C component has a simple set of APIs to configure it, and to read and write data via I2C. These APIs execute functions which implement the I2C interface.

You will use the function i2c_master_write_reg to configure the ADC at startup. Using the tx8 function, it writes the Slave device ID, address and then a list of bytes.

  1. Open module_i2c_master/src/i2c-mm.xc.
  2. Find the i2c_master_write_reg function.
  3. Double click on the first instance of tx8 to mark it.
  4. Right-click on the marked tx8 and select Open Declaration.

    The Editor window jumps to the tx8 function that implements the I2C protocol for writing a single byte.

    The HighPulse() function sends each bit of the byte in a loop. The timing of the edges of the I2C SCL (clock) and I2C SDA (data) signals are controlled by the function waitQuarter which, like the wait function you implemented previously, uses a timer to emit an event in the future upon which the signal level is changed.

This is a basic example of a hardware interface implemented completely in software running on a logical core.

Using I2C in the PWM controller

This section shows how to modify the PWM controller function so that it reads the ADC via I2C every period of the LED cycle.

  1. Declare the variables you need above your printstr statement, and use the i2c_master_write_reg function to initialize the ADC.
    // I2C write data
      unsigned char wr_data[1]={0x13};
      unsigned char rd_data[2];
      int adc_value;
    
      //Write configuration information to ADC
      i2c_master_write_reg(0x28, 0x00, wr_data, 1, i2cOne);
    
      printstr("Welcome to the XMOS PWM tutorial\n");
    

    Show where the i2c_master_write_reg function goes

  2. Add the following code to use the I2C_master_rx function to read from the ADC at the end of the PWM cycle period. It should be inserted after the duty cycle is incremented (duty_cycle += delta;)
    if (duty_cycle > period)
    {
      //Read ADC value using I2C read
    
      rd_data[0]=rd_data[0]&0x0F;
      i2c_master_rx(0x28, rd_data, 2, i2cOne);
      rd_data[0]=rd_data[0]&0x0F;
      adc_value=(rd_data[0]<<6)|(rd_data[1]>>2);
      printstr("Temperature is :");
      printintln(linear_interpolation(adc_value));
    

    Show where the i2c_master_rx code goes

  3. Add the linear interpolation function above your pwm_controller. This function is used to convert the ADC value to a temperature value.
    int TEMPERATURE_LUT[][2]= //Temperature Look up table
    {
      {-10,845},{-5,808},{0,765},{5,718},{10,668},
      {15,614},{20,559},{25,504},{30,450},{35,399},
      {40,352},{45,308},{50,269},{55,233},{60,202}
    };
    
    int linear_interpolation(int adc_value)
    {
      int i=0,x1,y1,x2,y2,temper;
      while(adc_value<TEMPERATURE_LUT[i][1])
      {
        i++;
      }
      // Calculating Linear interpolation using the formula
      // y=y1+(x-x1)*(y2-y1)/(x2-x1)
      x1=TEMPERATURE_LUT[i-1][1];
      y1=TEMPERATURE_LUT[i-1][0];
      x2=TEMPERATURE_LUT[i][1];
      y2=TEMPERATURE_LUT[i][0];
      temper=y1+(((adc_value-x1)*(y2-y1))/(x2-x1));
    
      return temper;
    }
    

    Show where the interpolation function goes

  4. Build and run your application.

    Every two seconds the temperature is printed to your console. Check that the temperature is printed to your console.

    Output temperature reading

    /files/images/16044/10/temperature.png

Next Steps

Congratulations! You have now used both the PWM and I2C xSOFTip, and used them to build and run a simple example. Both these interfaces are implemented in software, so you can view the code and make any changes you may wish to make. They rely on the deterministic, real-time capabilities of the xCORE architecture to deliver low latency, easy to use IP functions for use in your design.

We recommend that you:

  • Browse xSOFTip
    and take a look at the other xSOFTip components. You can read through the documentation in the Developer Column, and use them in your project. XMOS and our partners are working on new xSOFTip components all the time. Some components you see here are Roadmap components which are in our development plan. If there is a component you require for your system, please let us know – we’d love to hear from you.
  • Run the Example Applications for your sliceKIT Cards (look under sliceKIT in the xSOFTip Explorer). Each sliceKIT sliceCARD has its own Quick Start Guide to guide you through the demo application.
  • Try some other tutorials: A range of tutorials are available covering the xTIMEcomposer tools, xSOFTip and xKIT development boards. See Help > Tutorials.
  • Take a look at www.xmos.com/slicekit
    to view other sliceCARDs for use with your applications.

sliceKIT Development Board Tutorial Read More »

XC-1A Development Board Tutorial

Introduction

The XC-1A is a low-cost development board based on the XMOS XS1-G4 device. It
includes a single G4 device, 4Mbits SPI FLASH memory, 16 user-configurable LEDs, four push buttons, a speaker, JTAG and serial interfaces, four expansion areas suitable for IDC headers and a through-hole prototyping area for connecting external components..

This tutorial shows you how to write some simple XC programs that control
and respond to the XC-1A board components. In this tutorial you learn how to:

  • illuminate an LED on the board
  • flash an LED at a fixed rate
  • send the message“Hello World“ to your PC over a serial link
  • create multiple concurrent threads that flash LEDs at different rates
  • send a token between multiple threads, each flashing an LED in sequence
  • add a button listener thread that changes the LED color

Illuminate an LED

This part of the tutorial shows you how to illuminate an LED on your XC-1A, using an
XC port and an output statement.

Create a project

Before writing any code, you need a project to store your files in. To create a new project follow these steps:

  1. Choose FileNewXDE Project (button new project) to open the New XDE Project dialog.

    Alternatively, click here to automate steps 1, 3 and 4.

  2. In Project Name, enter a name such as illuminate.
  3. In Target Hardware, select the option XC-1A Development Board.
  4. In Application Software, select the option Empty XC File.
  5. Click Finish.

    The XDE creates a new project, then adds an empty source file to the project and opens it in the editor.

    /files/images/14223/11/app.png

Add the code

The program below illuminates an LED on an XC-1A.

#include <xs1.h>

out port bled = XS1_PORT_4C;

int main () {
  bled <: 0b0001;
  while (1)
    ;
  return 0;
}

Copy and paste the code into your project, and then choose FileSave (button save) to save your changes to file.

Examine the code

Take a look at the code in the editor. The declaration

out port bled = XS1_PORT_4C;

declares an output port named bled, which refers to the 4-bit port 4C. On the XC-1A, the I/O pins of
port 4C are connected to the LEDs positioned next to the four press-buttons (collectively referred to as button-LEDs) that contain green diodes.

Show image of port map…

Ports must be declared as global variables. The optional out qualifier allows the compiler to check for correct usage, thereby helping to reduce programming errors.

XC input and output statements make it easy to express I/O operations on ports.
The statement

bled <: 0b0001;

causes the value specified to the right of <: to be output to the port specified
to its left (led). The port then drives LED next to button A high and the other LEDs low,
causing the LED to illuminate green and the other LEDs to remain off.

The empty while loop prevents the program from terminating,
which ensures that the LED remains illuminated.

Build and run your project

To build and run your project, follow these steps:

  1. In the Project Explorer, click your project to select it, and then choose the menu option ProjectBuild Project (button build).

    The XDE displays its progress in the Console. When the build is complete,
    the XDE adds the compiled binary file to the subfolder bin/Debug.

    /files/images/14223/11/bin.png

  2. Choose RunRun Configurations.
  3. In the Run Configurations dialog, in the left panel, double-click XCore Application.
  4. In the right panel, in Name, enter the name illuminate.
  5. In Project, ensure that your project is displayed. If not,
    click Browse to open the Project Selection dialog, select your project, and then click OK.
  6. In C/C++ Application, click Search Project to open the Program Selection dialog,
    select your application binary, and then click OK.
  7. In Device options, in Run on, select the option hardware, and
    in Target, ensure that the option “XMOS XC-1A Board” is
    selected.

    If your hardware is not displayed, ensure that your XC-1A is
    connected to your PC, and then click Refresh list.

  8. Click Run to save your configuration and run it.

    The XDE loads the binary onto your XC-1A, displaying its progress in
    the Console. When the binary is loaded, the Console is cleared.

  9. On your XC-1A, verify that BUTTONLED A is illuminated green.
  10. In the Console, click the Terminate button (button stop) to
    stop your application running.

Exercise

To complete this part of the tutorial, perform the following steps:

  1. Modify your code so that it illuminates all four BUTTONLEDs.

    You should change the value output to the port bled so that all four LEDs are driven high.

    #include <xs1.h>
    
    out port led = XS1_PORT_4C;
    
    int main () {
      bled <: 0b1111;
      while (1)
        ;
      return 0;
    }
    
  2. Click the Run button (button run) to reload your last Run Configuration.

    The XDE determines that your source code has been updated and re-builds it,
    displaying progress in the Console.

    If your code contains errors, the XDE displays a dialog asking if you want to
    continue launching the application. Click No, locate the first error in the
    Console and double-click it to go to the offending line in the editor. When
    you have fixed all errors, re-run your application.

  3. On your XC-1A, verify that all four BUTTONLEDs are illuminated, and then click the
    Terminate button (button stop) to stop your application running.

Flash an LED

This part of the tutorial shows you how to flash an LED at a fixed rate, using an XC
timer and an input statement.

Create a new project

  1. Choose FileNew XDE Project (button new application).
  2. In the New Application dialog, in Application Name, enter the name flash.
  3. In Project, ensure that your project is selected.
  4. In Target Hardware, select the option XC-1A Development Board.
  5. In Application Software, select the option Empty XC File.
  6. Click Finish.

    The XDE creates a new project, then adds an empty source file to the project and opens
    it in the editor.

Add the application code

The program below flashes a single LED on an XC-1A.

#include <xs1.h>

#define FLASH_PERIOD 20000000

out port bled = XS1_PORT_4C;

int main (void) {
  timer tmr;
  unsigned isOn = 1;
  unsigned t;
  tmr :> t;
  while (1) {
    bled <: isOn;
    t += FLASH_PERIOD;
    tmr when timerafter (t) :> void;
    isOn = !isOn;
  }
  return 0;
}

Copy and paste the code into your project, and then choose FileSave (button save) to save your changes to file.

Examine the code

Take a look at the code in the editor. The declaration

timer tmr;

declares a variable named tmr, and allocates an available hardware timer. Each core on the
G4 device provides 10 timers, which can be used to determine when an event happens, or to
delay execution until a particular time. Each timer contains a 32-bit
counter that is incremented at 100MHz and whose value can be input at any time.

The statement

tmr :> t;

inputs the value of tmr‘s counter into the variable t. Having recorded the
current time, the statement

t += FLASH_PERIOD;

increments this value by the required delay, and the statement

tmr when timerafter(t) :> void;

delays inputting a value until the specified time is reached. The input
value is not needed, which is expressed as an input to void.

Build and run your application

To build and run your application, follow these steps:

  1. In the Project Explorer, click your project to select it, and then choose the menu option ProjectBuild Project (button build).

    The XDE builds your project, displaying its progress in the Console.
    When the build is complete, the XDE adds the compiled binary file to the application subfolder bin/Debug.

  2. Create a new Run Configuration for your project named flash, and run it.

    Show reminder…

  3. On your XC-1A, verify that BUTTONLEDA is flashing on-off, and then click the
    Terminate button (button stop) to stop your application running.

Switch between projects

The Run button (button run) can be used to switch between projects.
To complete this part of the tutorial, follow these steps:

  1. Click the arrow to the right of the Run button and select the
    Run Configuration named illuminate.
  2. On your XC-1A, verify that all four LEDs are illuminated.
  3. Click the arrow to the right of the Run button and select the
    Run Configuration named flash.
  4. On your XC-1A, verify that BUTTONLEDA is flashing on-off.
  5. Modify the source of the flashing LED application to change the value of FLASH_PERIOD from
    20000000 to 40000000.
  6. To build and run, just click the Run button.

    The XDE launches the Run Configuration you most recently selected.

  7. On your XC-1A, verify that BUTTONLEDA is flashing on-off at half the rate it was flashing previously,
    and then click the Terminate button (button stop) to stop your application running.

Exercise

To complete this part of the tutorial, perform the following steps:

  1. Modify your flashing LED application so that both BUTTONLEDA and BUTTONLEDB are flashed,
    with BUTTONLEDA flashed twice as fast as BUTTONLEDB.

    Show a tip…

    Explain the solution in more detail…

    Show a sample answer…

  2. Run your application.
  3. On your XC-1A, verify that the two LEDs are flashing at different speeds, and then click the
    Terminate button (button stop) to stop your application running.

This part of the tutorial shows you how to implement a UART protocol that transmits a message
from the XS1-G4 to your PC over a serial link. The XC-1A has a chip that performs
a USB-to-serial conversion. When the board is connected to a PC using a USB cable,
this chip presents a virtual COM port that can be interfaced using a terminal emulator.
(Currently on MACs, the virtual COM port cannot be supported at
the same time as the JTAG interface, preventing you from completing the following exercise.)

Create a project

The program below inputs from one of two timers in a loop.

#include <xs1.h>
#define BIT_RATE 115200
#define BIT_TIME XS1_TIMER_HZ / BIT_RATE
out port TXD = XS1_PORT_1H;

int main(){
  return 0;
}

void txByte (out port TXD, int byte) {
  unsigned time;
  timer t;

  /* input initial time */
  t :> time;

  /* output start bit */
  TXD <: 0;
  time += BIT_TIME;
  t when timerafter (time) :> void;

  /* output data bits */
  for (int i=0; i <8; i++) {
      TXD <: >> byte;
      time += BIT_TIME;
      t when timerafter (time) :> void;
  }

  /* output stop bit */
  TXD <: 1;
  time += BIT_TIME;
  t when timerafter (time) :> void;
}

Before continuing to the next part of this tutorial, create a new project using this code.

Show reminder…

Examine the code

A UART translates data between parallel and serial forms for transmission over a
serial link. Each bit of data is driven for a fixed period, during which time the
receiver must sample the data. The diagram below shows the transmission of a single byte of
data at a rate of 115200 bits/s:

/files/images/14223/11/uart-drive.png

The quiescent state of the link is high. A byte is sent by first driving a start bit (0),
followed by the data bits and then a stop bit (1). A rate of 115200 bits/s means that
each bit is driven for 1/115200 = 8:68us.

The program serializes a byte of data and transmits
its individual bits over a 1-bit port using the UART transmission protocol.

The function txByte outputs a byte by first outputting a start bit, following by a
conditional input on a timer that waits for the bit time to elapse; the data bits and
stop bit are output in the same way.

The output statement in the for loop

TXD <: >> byte ;

includes the modifier >>, which right-shifts the value of byte by the port width (1 bit)
after outputting the least significant port-width bits. This operation is performed in
the same instruction as the output, making it more efficient than shifting the value
as a separate operation afterwards.

Exercise

To complete this part of the tutorial, perform the following steps:

  1. Load a terminal emulator program on your PC and connect it to the virtual COM
    port provided by the XC-1A. A simple terminal emulator is available from the
    XMOS community website.
  2. Complete the program by declaring a port for the UART and by writing a main
    function that outputs the message Hello World! to this port.

    You can find the relevant port in the XC-1A Hardware Manual
    .

  3. Run your application.
  4. The terminal should receive and display the message.

Flash and cycle LEDs at different rates

This part of the tutorial shows you how to flash multiple CLOCKLEDs concurrently on your XC-1A.

Create an application

The program below flashes a single CLOCKLED.

#include <platform.h>
#define PERIOD 20000000

out port cled0 = PORT_CLOCKLED_0;
out port cled1 = PORT_CLOCKLED_1;
out port cled2 = PORT_CLOCKLED_2;
out port cled3 = PORT_CLOCKLED_3;
out port cledG = PORT_CLOCKLED_SELG;
out port cledR = PORT_CLOCKLED_SELR;

void flashLED (out port led, int period);

int main (void) {
  par {
    on stdcore [0]: { cledG <: 1;
                        flashLED (cled0 , PERIOD);
                    }
    on stdcore [1]: flashLED (cled1, PERIOD);
    on stdcore [2]: flashLED (cled2, PERIOD);
    on stdcore [3]: flashLED (cled3, PERIOD);
  }
  return 0;
}

void flashLED (out port led, int period){
}

Before continuing to the next part of this tutorial, create a new project using this code.

Examine the application code

The schematic for the 12 clock-LEDs is shown below; the location of the ports are shown in
the XC-1A Hardware Manual
.

/files/images/14223/11/circuit.png

The LED anodes are connected to four 8-bit ports: PORT_CLOCKLED_0 on XCore 0, PORT_CLOCKLED_1 on XCore 1,
PORT_CLOCKLED_3 on XCore 3 and PORT_CLOCKLED_4 on XCore 4. The LED cathodes are connected to
two 1-bit ports on XCore 0: PORT_CLOCKLED_SELG (green) and PORT_CLOCKLED_SELR (red). This means that
two pins must be driven to illuminate a clock-LED.

The par statement provides a simple way to create concurrent threads that run
independently of one another.

The on statement instructs the compiler on which processor each port is connected and
each thread is executed. An on statement may only be used with threads created by main,
in which case main may contain only channel declarations, a single par statement
and an optional return statement.

The program creates four concurrent threads, each running an instance of a function flashLED.

Exercise 1

To complete this part of the tutorial, perform the following tasks:

  1. Implement the body of the function flashLED so that it flashes a single LED.
    Note that the pins are connected to bits 4–6 on the 8-bit port.
  2. Build your application, create a new Run Configuration, and run it.
  3. On your XC-1A, verify that four LEDs on the clockface flash continually at a fixed rate, and then click
    the Terminate button (button stop) to stop your application running.

Exercise 2

To complete this part of the tutorial, perform the following tasks:

  1. Experiment with different period values for each of the threads so that the threads
    can be seen to be operating independently of one another.

Run tasks concurrently

This part of the tutorial shows you how to use XC channels to flash eight of the LEDs
on your XC-1A in the round robin sequence..

Create an application

The program below flashes a single CLOCKLED.

#include <platform.h>
#define PERIOD 20000000

out port cled0 = PORT_CLOCKLED_0;
out port cled1 = PORT_CLOCKLED_1;
out port cled2 = PORT_CLOCKLED_2;
out port cled3 = PORT_CLOCKLED_3;
out port cledG = PORT_CLOCKLED_SELG;
out port cledR = PORT_CLOCKLED_SELR;

void tokenFlash (chanend left, chanend right, out port led, int delay, int isMaster) {
  timer tmr;
  unsigned t;

  if (isMaster) /* master inserts token into ring */
    right <: 1;

  while (1) {
    int token;
    left :> token; /* input token from left neighbor */
    led <: 1;
    tmr :> t;
    tmr when timerafter (t+ delay ) :> void;
    led <: 0;
    right <: token; /* output token to right neighbor */
  }
}

int main (void) {
  chan c0, c1, c2, c3;
  par {
    on stdcore [0]: { cledG <: 1;
                      tokenFlash (c0, c1, cled0, PERIOD, 1);
                    }
    on stdcore [1]: tokenFlash (c1, c2, cled1, PERIOD, 0);
    // other cores
  }
  return 0;
}

Before continuing to the next part of this tutorial, create a new project using this code.

Examine the code

An XC channel provides a synchronous, bidirectional link between two threads. It
consists of two channel ends, which two threads can use to interact on demand using
the XC input and output statements.

The function tokenFlash implements a component of the token ring illustrated below,
repeatedly inputting a token from its left neighbor, flashing an LED and outputting
the token to its right neighbor:

/files/images/14223/11/tokenring.png

The first two function parameters are channel ends, the third an LED port and the
fourth a Boolean value indicating whether or not the thread executing the function is
the designated master. The master inserts a token into the ring.

The XC input and output statements are used to communicate the token between
threads. As channels are synchronous, each output operation blocks until a matching
input operation is ready, ensuring that precisely one thread has possession of the
token at any time.

The main function constructs the token ring. A channel is declared using the keyword
chan. The locations of its two channel ends are established through its use in two
statements of the par.

A total of four channels are required to complete this program, each of which must
be used in two threads: once as a left argument and once as a right argument to the
function tokenFlash.

Exercise 1

To complete this part of the tutorial, perform the following tasks:

  1. Modify the function tokenFlash so that it flashes each of the three LEDs connected
    to its port in sequence, add the required port declarations and complete the definition
    of main.
  2. Build your application, create a new Run Configuration, and run it.
  3. On your XC-1A, verify that the 12 LEDs each flash in sequence as the token cycles around the four threads,
    and then click the Terminate button (button stop) to stop your application running.

Use a button to change the LED color

This part of the tutorial shows you how to detect a button press and respond to
it by changing the color of the LED cycling around the clockface, using the XC
select statement.

Examine the code

A select statement is used to respond to one of a set of inputs, depending on which
becomes ready first. If more than one input becomes ready at the same time, only
one is executed.

The function below waits for either a token to be received, in which case it passes it
on, or for a button to be pressed.

void buttonListener ( chanend left ,
                      chanend right ,
                      in port button ,
                      out port g,
                      out port r) {
  int token ;
  int isGreen = 1;
  g <: 1;
  while (1)
    select {
      case left :> token :
      /* pass token on */
        right <: token ;
      break ;
      case button when pinsneq (0xf):> void :
        /* change color *
        ...
      break ;
  }
}

The guarded input statement

case left :> token :

becomes ready when the token arrives, in which case it is input and passed to the
next thread. The guard

case button when pinsneq (0xf):> void :

becomes ready when the value on the pins connected to the port button is not equal
to the bit pattern 0xf. This signifies that the button was pressed.

Exercise 1

To finish this part of the tutorial, complete the following tasks which build on the
program from the previous section:

  1. Add a declaration for the button port (see the XC-1A Hardware Manual
    for details of the initializer).
  2. Complete the function buttonListener and integrate it into the token ring.

    Note that you cannot output to a port in two concurrent threads, nor can you write
    the same variable in parallel. These restrictions prevent common programming
    errors such as race conditions, and ensure that the two threads can be run on any
    two cores, regardless of whether they share memory.

  3. Run your application.

    Press one of the buttons to change the colour of an LED as it circulates around the clock.

  4. On your XC-1A, verify that pressing a button changes the colour of an LED as it circulates,
    and then click the Terminate button (button stop) to stop your application running.

This tutorial provides only a basic introduction the XC-1A hardware.

For more information on the board refer to the XC-1A Hardware Manual
.

For more information on programming in XC see Programming XC on XMOS Devices
.

XC-1A Development Board Tutorial Read More »

Scroll to Top