OTP Custom Boot Loader for 8 bit flash

Technical questions regarding the XTC tools and programming with XMOS.
richard
Respected Member
Posts: 318
Joined: Tue Dec 15, 2009 12:46 am

Post by richard »

The xmap option --first can be used to specify that a specific object should be the first object linked.

If you pass -v to the compiler you can see how xcc itself uses this option to ensure the crt1.o is linked first:

Code: Select all

 $ xcc test.o -target=XK-1A -save-temps -v -Wno-timing
 xmap --defsymbol _default_clkblk=0x6 -march=xs1b -o "a.xe" "-targetfile=/home/richard/XMOS/xTIMEcomposer/12.2.0/targets/XK-1A/XK-1A.xn" -L "/home/richard/XMOS/xTIMEcomposer/12.2.0/target/lib/xs1b" -L "/home/richard/XMOS/xTIMEcomposer/12.2.0/target/lib" --first "/home/richard/XMOS/xTIMEcomposer/12.2.0/target/lib/xs1b/crt1.o" --first "/home/richard/XMOS/xTIMEcomposer/12.2.0/target/lib/xs1b/crtbegin.o" -lxc -lxcc -lxs1 -lsyscall -lm -lc -lgcc -lstdc++ -lsupc++  "test.o" --last "/home/richard/XMOS/xTIMEcomposer/12.2.0/target/lib/xs1b/crtend.o" --last "/home/richard/XMOS/xTIMEcomposer/12.2.0/target/lib/xs1b/crtn.o"
To ensure _start is placed at the begining of the executable the symbol you should compile it to an object file with xcc -c and then link it in with xcc -Xmapper --first -Xmapper myobj.o.

To make this work using the Makefile you can add something along the following lines:

Code: Select all

# Exclude start.S to avoid it being linked in twice
EXCLUDE_FILES = start.S

# Custom rule for building start.o
start.o: src/start.S
	xcc $(XCC_FLAGS) -c $< -o $@

# Make the app depend on start.o
$(BIN_DIR)/$(APP_NAME).xe : start.o

# Link in start.o
XCC_MAP_FLAGS = -Xmapper --first -Xmapper ../start.o -nostdlib


GerhardNorkus
Active Member
Posts: 55
Joined: Wed Jan 05, 2011 2:15 pm

Post by GerhardNorkus »

Hi Richard,

Although I needed to do a little tweaking to the supplied code...you and Henk are the best. The build files for Linux are a little finicky. I forgot that the build commands need to have a tab instead of just spaces. Then, there's this little thing in the common build files that prevents you from properly defining the debug paths (ugh!). If you want me to describe the problems, I will in a later post.

In the meantime, I have lines that properly compile now...and they generate some really tight code. For some functions, I needed to cobble my own versions of them because I do not know how properly to link in the proper external libraries at the moment (write_sswitch_reg, read_sswitch_reg, and get_local_tile_id). I simply used disassembly for this.

When inserting the lines listed below into the generated makefiles (by the xTIMEcomposer), these lines must appear before the EXCLUDE_FILES but after the XCC_FLAGS/XCC_MAP_FLAGS definitions. I've tested, and it seems this works well. I will be using the same format for my 'injection code'. The code that gets copied into upper memory.

I am working on the final stage of parsing through the XE files that get saved in the 8 bit Flash EEPROM. This loader will simply pause if there are no usable images on the EEProm. All the EEProms will be preprogrammed with a minimal USB loader using a separate programming unit. During final assembly, upgraded xe files can be burned using this USB loader. In the event of a bug, all of our cameras have a micro-JTag port to allow programming. And the intent is to have a loader that is under 2K in size, although it would be nice to have the AES module as a part of this. You know...code security? I will post this loader (not the 8 bit portion, just the framework) and a full set of documentation as a project a little later next month.

Do you have any suggestions? Can you peek at the _start routine shown in the earlier post and let me know what would be good to do with the stack pointers, etc? I could probably just look at a disassembly of a normal program and use that....but it's faster to ask someone with experience. I noticed that the compiler generates PORTNAME.ctor and PORTNAME.dtor functions for the initialization and cleanup of the ports. Is this a convention that is expected to stay?

Thanks again!
Gerhard Norkus

Code: Select all

# Our custom build rule..remember, before 'xcc' there should be a tab,
# not a space.  Otherwise it will give missing separator for linux makefiles
ifeq "$(CONFIG)" "Debug"
.build_Debug/src/start.xc.o: src/start.xc
	xcc $(XCC_FLAGS_Debug) -c $< -o $@
$(BIN_DIR)/$(APP_NAME)_Debug.xe: .build_Debug/src/start.xc.o
endif
ifeq "$(CONFIG)" "Release"
.build_Release/src/start.xc.o: src/start.xc
	xcc $(XCC_FLAGS_Release) -c $< -o $@
$(BIN_DIR)/$(APP_NAME).xe: .build_Release/src/start.xc.o
endif

# The EXCLUDE_FILES is for custom make rules to avoid files being linked twice
EXCLUDE_FILES = start.xc


GerhardNorkus
Active Member
Posts: 55
Joined: Wed Jan 05, 2011 2:15 pm

Post by GerhardNorkus »

OOPS! Forgot to show the map flags with that stuff

Code: Select all

XCC_MAP_FLAGS_Debug   = -nostdlib -Xmapper --first -Xmapper ".\..\.build_Debug\src\start.xc.o"
XCC_MAP_FLAGS_Release = -nostdlib -Xmapper --first -Xmapper ".\..\.build_Release\src\start.xc.o"

# Our custom build rule..remember, before 'xcc' there should be a tab,
# not a space.  Otherwise it will give missing separator for linux makefiles
ifeq "$(CONFIG)" "Debug"
.build_Debug/src/start.xc.o: src/start.xc
	xcc $(XCC_FLAGS_Debug) -c $< -o $@
$(BIN_DIR)/$(APP_NAME)_Debug.xe: .build_Debug/src/start.xc.o
endif
ifeq "$(CONFIG)" "Release"
.build_Release/src/start.xc.o: src/start.xc
	xcc $(XCC_FLAGS_Release) -c $< -o $@
$(BIN_DIR)/$(APP_NAME).xe: .build_Release/src/start.xc.o
endif

# The VERBOSE variable, if set to 1, enables verbose output from the make system.
VERBOSE = 1

XMOS_MAKE_PATH ?= ../..
-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common

# The EXCLUDE_FILES is for custom make rules to avoid files being linked twice
EXCLUDE_FILES = start.xc
GerhardNorkus
Active Member
Posts: 55
Joined: Wed Jan 05, 2011 2:15 pm

Post by GerhardNorkus »

O.K. Now I know I added the code properly, and here goes again... This is a first stage loader. It is designed to inject code into upper memory, but it can be used to create the injected code also, as a second project. It compiles properly (but doesn't run if the xn file has the oscillator specified...dunno why!). It needs to have the stack pointer (sp) assigned to a proper location, and any suggestions are welcome. Criticisms welcome too : )

Start.xc

Code: Select all

#include <xs1.h>
#include <print.h>
#include "CopyCode.h"

// Entry point...  How do I make sure this always appears first
// in the
void _start(void)
{
	// This initializes the code and data segment pointers
	// I suppose I will need to also initialize the stack pointer eh?
	// Any good suggestions?
	asm("ldap r11, _cp");
	asm("set cp, r11");
	asm("ldap r11, _dp");
	asm("set dp, r11");


	// This calls the main routine
	main();
}
Main.xc

Code: Select all

#include <xs1.h>
#include <print.h>
#include "CopyCode.h"

// The following array is binary code for the XS1 and causes port
// 1L to flash.  If you save this in a binary editor, you can use
// "xobjdump -D filename" to disassemble the array of bytes and 
// see what it does.
unsigned short uBinArr[58] = {
 0x7741,0xF000,0xD82B,0x37FB,0xF000,0xD832,0x37EB,0xD000,0x7743,0x5502,0x5541,0x5580,0x86C1,0xE801,0xB6C4,0xF000
,0x5880,0xA6DD,0xF000,0x6EC0,0x6900,0xF000,0x6D41,0xAECE,0x14E7,0x1718,0xE809,0xB708,0xAF02,0x1255,0x16D4,0xE809
,0xB708,0x770B,0xF000,0x6C02,0xE808,0xF000,0x6846,0xFED4,0x0FEC,0x77C0,0xF000,0x6C02,0xE800,0x77C0,0x2D00,0x0131
,0x5A00,0x0262,0x0B00,0x0001,0xB044,0x0001,0xB054,0x0001,0x0B00,0x0001};

int main(void)
{
	// This initializes the code and data segment pointers
	// I suppose I will need to also initialize the stack pointer eh?
	// Any good suggestions?
	asm("ldap r11, _cp");
	asm("set cp, r11");
	asm("ldap r11, _dp");
	asm("set dp, r11");

	// This calls the main routine.  
	// 0x1B000 is where in memory the code gets copied to and started
	// 58 is the number of elements in the array
	// uBinArr is an array of shorts containing the binary to be executed
	CopyCode(0x1B000, 58, uBinArr);
}
CopyCode.S (make sure you have the 'S' capitalized to allow for the xc preprocessor to go through the comments, etc...)

Code: Select all

	.text
	.align	2

// void CopyCode(int nJumpLoc, int nCodeLen, unsigned short uBinArr[])
// r0 is the jump/copy address
// r1 is the code length
// r2 is the source array
// r3, and r11 do not need to be saved by the callee
	.globl	CopyCode
	.align	2
	.type	CopyCode,@function
	.set	CopyCode.nstackwords,0
	.globl	CopyCode.nstackwords
	.cc_top CopyCode.function
CopyCode:

	sub r1, r1, 1
	ld16s r3, r2[r1]
	st16 r3, r0[r1]
	bt r1, CopyCode

	bau r0
	retsp 0
	.cc_bottom CopyCode.function

CopyCode.h

Code: Select all

#ifndef _copycode_h
#define _copycode_h

#ifdef __XC__
void CopyCode(int nJumpLoc, int nCodeLen, unsigned short uBinArr[]);
#else
void CopyCode(int nJumpLoc, int nCodeLen, unsigned short *uBinArr);
#endif
int main(void);

#endif


Makefile

Code: Select all


# The TARGET variable determines what target system the application is
# compiled for. It either refers to an XN file in the source directories
# or a valid argument for the --target option when compiling
TARGET = XS1-U8A-64-FB96-C5

# The APP_NAME variable determines the name of the final .xe file. It should
# not include the .xe postfix. If left blank the name will default to
# the project name
APP_NAME = EnvisicONFILoader

# The USED_MODULES variable lists other module used by the application.
USED_MODULES = 

# The flags passed to xcc when building the application
# You can also set the following to override flags for a particular language:
# XCC_XC_FLAGS, XCC_C_FLAGS, XCC_ASM_FLAGS, XCC_CPP_FLAGS
# If the variable XCC_MAP_FLAGS is set it overrides the flags passed to
# xcc for the final link (mapping) stage.
XCC_FLAGS_Debug = -O0 -g
XCC_FLAGS_Release = -O3 -g

# The debug version does not have the --bootable flag, since I am using
# it for testing.  If someone wants to use this sample to compile the
# binary that eventually gets converted to text, use the --bootable
# flag to create and xe file with just the binary.  Then use xobjdump --split myfile.xe
# to get image_n0c0.bin.  Convert the bin file to a text array of shorts and
# use this as the array in the main.xc file.  If one is compiling code to
# be injected, the following flags need to be added to both the debug and
# release paths (-Xmapper --image-base -Xmapper 0x0001B000).  This allows
# the code to be compiled to appear at whatever upper memory location you
# desire.
XCC_MAP_FLAGS_Debug   = -nostdlib -Xmapper --first -Xmapper ".\..\.build_Debug\src\start.xc.o"
XCC_MAP_FLAGS_Release = -nostdlib --bootable -Xmapper --first -Xmapper ".\..\.build_Release\src\start.xc.o"

# Our custom build rule..remember, before 'xcc' there should be a tab,
# not a space.  Otherwise it will give missing separator for linux makefiles
ifeq "$(CONFIG)" "Debug"
.build_Debug/src/start.xc.o: src/start.xc
	xcc $(XCC_FLAGS_Debug) -c $< -o $@
$(BIN_DIR)/$(APP_NAME)_Debug.xe: .build_Debug/src/start.xc.o
endif
ifeq "$(CONFIG)" "Release"
.build_Release/src/start.xc.o: src/start.xc
	xcc $(XCC_FLAGS_Release) -c $< -o $@
$(BIN_DIR)/$(APP_NAME).xe: .build_Release/src/start.xc.o
endif

# The VERBOSE variable, if set to 1, enables verbose output from the make system.
VERBOSE = 1

XMOS_MAKE_PATH ?= ../..
-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common

# The EXCLUDE_FILES is for custom make rules to avoid files being linked twice
EXCLUDE_FILES = start.xc

XS1-U8A-64-FB96-C5.xn - a stripped down version of it. For some reason, setting the Oscillator="24MHz" in the <Node Id> section kills the app produced. Any suggestions???

Code: Select all

<?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>XS1-U8A-64-FB96-C5 Device</Name>

  <Declarations>
    <Declaration>tileref tile[1]</Declaration>
  </Declarations>

  <Packages>
    <Package id="0" Type="XS1-UnA-64-FB96">
      <Nodes>
        <Node Id="0" InPackageId="0" Type="XS1-L8A-64" SystemFrequency="500MHz" ReferenceFrequency="100MHz">
          <Tile Number="0" Reference="tile[0]"/>
        </Node>
      </Nodes>
    </Package>
  </Packages>

  <JTAGChain>
     <JTAGDevice NodeId="0"/>
  </JTAGChain>

</Network>
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am

Post by segher »

You can put the stack anywhere in memory you like; the
normal location is at the top, 1ff00 or 1ff80 (the debug routines
in rom use the memory above that).

Initialising sp inside the compiled main() won't work: the
compiler likely starts that routine by setting up a stack frame,
e.g. "entsp 1", and that writes to the stack. So instead you
should write that in assembler (set up cp and dp there as
well, nothing prevents the compiler from accessing via cp/dp
before you set those up in the inline asm).

Oh, nit: the retsp in your copy code is dead code.
GerhardNorkus
Active Member
Posts: 55
Joined: Wed Jan 05, 2011 2:15 pm

Post by GerhardNorkus »

Thanks, segher : )

What does nit: mean? Don't know if it is an acronym from across the pond or not.

It appears my cp and dp set in the main is also dead (or useless repetition) of code. It's already in the start!

I got my ONFI flash reader code working... I discovered initializer methods that go with the ports. They appear in every xc.o file that has a declared port. Additionally, the methods are not globally visible, only from within the xc.o file. An example is as follows:

out buffered port:32 myport=XS1_PORT_1F;

generates the following

Code: Select all

<myport.ctor>:
             0x0001070a: 00 f0 0b 58: ldw (lru6)      r0, dp[0xb]
             0x0001070e: 08 e8:       setc (ru6)      res[r0], 0x8
             0x00010710: 80 f0 0f e8: setc (lru6)     res[r0], 0x200f
             0x00010714: 60 68:       ldc (ru6)       r1, 0x20
             0x00010716: d4 fe ec 27: settw (lr2r)    res[r0], r1
             0x0001071a: 00 f0 46 68: ldc (lru6)      r1, 0x6
             0x0001071e: d4 fe ec 0f: setclk (lr2r)   res[r0], r1
             0x00010722: c0 77:       retsp (u6)      0x0
For my own purposes, I call everything EXCEPT the reference clock initializer. For some reason, touching that constructor causes it to stop!

Anyway, I am avoiding processing the ELF files in my loader at the moment. Not enough time, and I have a deadline for these 3D scanner prototypes (3 weeks). I will be compiling using the bootable flag, but for debugging purposes, it sure would be nice if I had some code in hand which already did the parsing of the ELF files. Instead, I will mimick the flash structure when copying firmware images. I've got a few megs to play with (about 2,000 of them)...

Eh hem....could I beg, borrow, and (well I guess not steal since it's open software, eh?) appropriate a copy of that from someone? I could use it over the holidays to make something that works with xflash?

Thanks,
GJN
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am

Post by segher »

A nit: a minor shortcoming, the object of nitpicking.

Those constructors are, well, constructors: the linker puts
addresses for all in an array, and the startup code is supposed
to call them all in order.

Could you show the generated code for the one that doesn't
work?

What do you need to parse from the ELF files -- *what*
ELF files? The final executables?