Copyright (c) Hyperion Entertainment and contributors.
Writing apps for Xena
Contents
Writing apps for Xena
Overview
An XMOS XS1-L2 paralell processing chip is included in each A-EON X1000 computer. Amigas have a history of naming their unique chips, and the XMOS chip in the X1000 has been named Xena.
The XS1-L2 is actually two XS1 cores in a single package. In the X1000, about 1/3 of the I/O pins are connected back to a "localbus" interface, with the rest brought out to a "Xorro" slot for connections to whatever the user wants to connect.
The Xena chip has many unique attributes. It can be programmed in "C", or in a variant called "XC" that better embraces the stream oriented architecture of the chip.
Each of the two cores in Xena supports up to eight threads. Each thread operates at a internal clock of 100,000,000 Hz. When programmed in XC, each threads memory is private to that thread. There are no "common" data areas between threads, and all inter-thread data is moved through Channels. These channels can carry data between threads, cores, and even between XMOS chips.
From the software side, each channel is declared, then used from exactly two places. Any data written to one end can be read from the other. Channels are bi-directional, so data can move in both directions.
Channels are BLOCKING. A read will stall until a matching write is made to satisfy it. Likewise a write will stall until a matching read is made at the other end of the channel. This will require some organization from the programmer.
The "streaming" modifier may be added to a channel declaration. This will dedicate a hardware channel exclusively to this connection. This may be required for higher bandwidth connections.
From the hardware side, channels may be three or five wire types. Cores are interconnected by a switching "fabric" that consists of these channels. Part of each *.xc file created configures the switches for the particular needs of an application.
Programming the Xena chip
We have a set of "XTools" for delivery of the executable to the Xena chip. At this time, the compiler suite is available only for X86 platforms. So all development is currently done from a Windows or X86 linux platform, and the resulting *.xe files are moved over to the X1000 by flash drive or network share. The XMOS development tools were recently updated to version 12, but since my existing projects were started under version 11, I'll provide platform definition files for both.
At the creation of each new project in the XMOS IDE, the target board must be selected. If you are using version 11 of the XDE, create directory C:\Program FIles\XMOS\DevelopmentTools\11.11.1\targets\A-EON X-1000 Xena Device and copy the following text into a file in that directory
<?xml version="1.0" encoding="UTF-8"?> <Network xmlns="http://www.xmos.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.xmos.com http://www.xmos.com"> <Type>Device</Type> <Name>A-EON X-1000 Xena Device</Name> <Declarations> <Declaration>core stdcore[2]</Declaration> </Declarations> <Packages> <Package ID="0" Type="XS1-L2A-QF124"> <Nodes> <Node Id="0" InPackageId="0" Type="XS1-L1A" Oscillator="25MHz" SystemFrequency="500MHz"> <Core Number="0" Reference="stdcore[0]"/> </Node> <Node Id="1" InPackageId="1" Type="XS1-L1A" Oscillator="25MHz" SystemFrequency="500MHz"> <Core Number="0" Reference="stdcore[1]"/> </Node> </Nodes> </Package> </Packages> <Links> <Link Encoding="5wire" Delays="0,1"> <LinkEndpoint NodeId="0" Link="XLG"/> <LinkEndpoint NodeId="1" Link="XLF"/> </Link> <Link Encoding="5wire" Delays="0,1"> <LinkEndpoint NodeId="0" Link="XLH"/> <LinkEndpoint NodeId="1" Link="XLE"/> </Link> <Link Encoding="5wire" Delays="0,1"> <LinkEndpoint NodeId="0" Link="XLE"/> <LinkEndpoint NodeId="1" Link="XLH"/> </Link> <Link Encoding="5wire" Delays="0,1"> <LinkEndpoint NodeId="0" Link="XLF"/> <LinkEndpoint NodeId="1" Link="XLG"/> </Link> </Links> <JTAGChain> <JTAGDevice NodeId="0"/> <JTAGDevice NodeId="1"/> </JTAGChain> </Network>
to allow selection of Xena projects. There were some changes when version 12 released, so if you are using the new "Tooltips" IDE, create the directory C:\Program FIles\XMOS\xTIMEcomposer\12.0.0beta\targets\A-EON X-1000 Xena Device and create a file there with these contents
<?xml version="1.0" encoding="UTF-8"?> <Network xmlns="http://www.xmos.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.xmos.com http://www.xmos.com"> <Type>Device</Type> <Name>A-EON X-1000 Xena Device</Name> <Declarations> <Declaration>tileref tile[2]</Declaration> </Declarations> <Packages> <Package ID="0" Type="XS1-L2A-QF124"> <Nodes> <Node Id="0" InPackageId="0" Type="XS1-L1A" Oscillator="25MHz" SystemFrequency="500MHz"> <Tile Number="0" Reference="tile[0]"/> </Node> <Node Id="1" InPackageId="1" Type="XS1-L1A" Oscillator="25MHz" SystemFrequency="500MHz"> <Tile Number="0" Reference="tile[1]"/> </Node> </Nodes> </Package> </Packages> <Links> <Link Encoding="5wire" Delays="0,1"> <LinkEndpoint NodeId="0" Link="XLG"/> <LinkEndpoint NodeId="1" Link="XLF"/> </Link> <Link Encoding="5wire" Delays="0,1"> <LinkEndpoint NodeId="0" Link="XLH"/> <LinkEndpoint NodeId="1" Link="XLE"/> </Link> <Link Encoding="5wire" Delays="0,1"> <LinkEndpoint NodeId="0" Link="XLE"/> <LinkEndpoint NodeId="1" Link="XLH"/> </Link> <Link Encoding="5wire" Delays="0,1"> <LinkEndpoint NodeId="0" Link="XLF"/> <LinkEndpoint NodeId="1" Link="XLG"/> </Link> </Links> <JTAGChain> <JTAGDevice NodeId="0"/> <JTAGDevice NodeId="1"/> </JTAGChain> </Network>
In both cases, these are just the normal XS1-L2 header files, with the addition of "OSCILLATOR=25MHZ" to each core/tile to specify the clock used for the X1000 Xena chip.
Unique to XC:
XC does not support pointers. Any RAM used by a thread may NOT be accessed by any other thread. Any I/O pins declared by a thread may not be used by any other thread.
main() in an XC program may only declare channels, and launch other threads with par() statement(s)
multiple core projects may only have one par() in main().
This may take some getting used to. Where normal "C" programs may gather data into a block of memory, then pass a pointer to it, XC threads cannot share access to the memory block, nor pass pointers.
So far, this has made my main() a simple "map" of the threads I am using, and the channels that connect them. It also makes code modules MUCH more easily re-used, since inter-thread dependencies are much better documented.
In some cases, (like the re-use of existing code), we simply MUST have pointers. Standard C can be mixed with XC, though you'll lose some of the elegance that XC provides. One example is the FAT file system support used in the logger application.
Connecting to the hardware
As delivered, the Xena chip in an X1000 includes the localbus connections, a built-in JTAG port for programming and debugging, and an LED from each XCORE. To make any connections to the chip, a XORRO card will be required. The Xorro card provides access to all the open pins, and to the JTAG pins in case you want to add additional XMOS chips.
To get much done, you'll probably need to add some accessories. The logger project adds a serial port, and an SD card interface.
WARNING: wiring your own circuit boards can be fun, but it does require certain skills. If you are not comfortable with a soldering iron, or familiar with electronics assembly, you might want to find someone who IS skilled in these things to assemble your Xorro project for you.
AmigaKit may provide Xorro assembly for pre-compiled projects like the Logger if requested to do so. Please contact them for price and availability.
The Xorro card provides 3.3, 5, and 12 volts for your projects. The I/O voltage of the chips is 3.3 volts. DO NOT apply voltages above 3.3 or below 0 volts to any input. DO use output buffers for any outputs coming from the XORRO slot. pins that are always INPUT or OUTPUT should be declared as such in your code. Bi-Directional pins will switch to output when any output statement is executed, (pin <: value) and will switch back to input when any read is made of that pin (pin :> state).
Project Description, Buffer Example
The buffer demonstration shown at AmiWest is available. Instructions for Xorro wiring are included. File:BufferExample.lha
Project Description, The Logger
The goal is to create a debug logger that will reside entirely on a Xorro card. It will connect directly to SER0: of the Nemo board, and will capture the debug stream and store it on an SD card for later retrieval.
The BufferExample code is the starting point. Adding the SD_4Bit file system from XCORE.com should provide the thread for managing the SecureDigital card.
Hardware requirements
A Xorro card for access to the Xena signals. A serial "level shifter" chip, 3.3 volt. (MAX3232) An SD card socket will all pins available (4 bit "fast" mode) An SD card. Faster is better, I'm using Class 10.
Please note that the MAX3232 and the SD Card are both 3.3 volt devices, so voltage translation is not necessary.
Software threads used, and their role. Fast serial Receive: from XCORE Fast serial Transmit: from XCORE SD card handler(with FAT FS): from XCORE Smart buffer: Code in the BufferExample Localbus, Xena side: code in the BufferExample Localbus, Nemo side: code in the BufferExample
The modules used: The fast serial routines, TX and RX, each require a single pin to be defined as the input or output. Each also requires a streaming channel end to supply or sink the data being handled. These "fast" serial routines are 8N1, no handshaking, and can use baud rates up to about 10 Megabaud, assuming the channels will be serviced quickly enough.
The SD Card code supports reading and writing files to a Secure Digital card. Either SPI or 4Wire modes may be chosen, and software to support FAT file system is included. There are a bunch of options that determine support for multiple devices, long file names, and other stuff. It should be noted that the FATFS code is written in C, not in XC as most other modules are.
The buffer code is my own, and was written to collect incoming bytes, until a large enough block is ready to pass on to the SD card thread. The code itself is quite simple, with the following design goals: The highest priority is to receive and buffer incoming bytes from the streaming input (from the Serial IN pin, in this case). When a block of data is requested, IF there is enough data, it is sent out the output streaming channel. Finally, one or optionally two control channels are checked for commands and handled as needed. There are commands to get the current buffer quantity, get the amount if idle time since the last received character, get the number of buffer overruns (this counter will reset on read), and to request n Bytes of data. There is an optional second control channel if requested.
The localbus code is two separate programs. One is a thread in CORE1, and the other is an app running on the X1000. This provides addressable communication between Xena and Nemo. The code presented here is early working code. I hope the community will work together to decide what features might be added to this example.
It should be noted that the XMOS localbus thread might block if any channel read or write is not ready. If this happens while the Data Bus output buffer is enabled, permanent damage can be caused to the Xena chip and/or the localbus. For this reason the localbus thread will wait on a timer while the bus output is enabled. This section of code is clearly marked as "KEEP OUT", and I suggest that you leave it alone unless you are SURE of what you are doing.
The localbus code currently moves uint16 data words at all times. It is possible to support 8, 16, and 32 bit values, but I wanted the first example to be as simple as possible.
Addresses for the localbus are 16 bits wide, and only even addresses are supported. Odd addresses will be decremented. So far I am using addresses to select which threads the localbus might exchange data with.
More will be provided as I get a chance. The code is up now. Questions are welcomed.. lylehaze@gmail.com