Skip to content

Sample μ'nSP project from scratch, μ'nSP Monitor

Notifications You must be signed in to change notification settings

GMMan/unsp-monitor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

μ'nSP Monitor/sample project

This is a minimal monitor program for μ'nSP MCUs with UART, and an example project with a minimal Makefile setup. It is targeted for GPL95101UB, but you can replace body files to target different μ'nSP MCUs.

You need the μ'nSP IDE to get the toolchain. Download it from here.

Walkthrough

This section will offer some info on the various parts of the project. Some command line arguments for the various tools are explained, and for those that aren't, please consult μ'nSP Programming Tools User's Manual.

Source files

The code is written in μ'nSP 1.3 assembly rather than C. Your project should have the following interrupt handlers declared public:

  • _BREAK
  • _FIQ
  • _RESET
  • _IRQ0
  • _IRQ1
  • _IRQ2
  • _IRQ3
  • _IRQ4
  • _IRQ5
  • _IRQ6
  • _IRQ7

At a minimum your firmware must have _RESET. If it's not present, the linker will crash.

__sn_sp_val is a pointer to the bottom of the SRAM, for putting your stack at. This symbol is automatically provided by the linker.

Generally you will want to include the body header file so you can use names and constants for registers.

A function looks like this:

FuncName:   .proc
    push bp to [sp]             // Save caller bp
    sp -= 1                     // Allocate locals
    bp = sp + 1                 // bp points to first local

    // Save first arg to local
    r1 = bp + 4                 // First arg is 3 past previous value of sp
                                // If you didn't save bp yet, it would have
                                // been sp + 3. Here, go past allocated local,
                                // saved bp, and sr/pc to get to the first arg
    [bp] = r1                   // Save to local, note you can use offsets
                                // when doing memory accesses with bp

    // Call another function
    sp -= 1                     // Allocate stack for args
    r2 = sp + 1                 // Get address of first arg
    [r2] = r1                   // Put value into arg
    call AnotherFunc            // Call the function
                                // Core pushes next pc and current sr to stack
                                // Return value is in r1
    sp += 1                     // Deallocate args

    sp += 1                     // Deallocate locals
    pop bp from [sp]            // Restore saved bp
    retf                        // Return from function

    // pop bp, pc from [sp]     // You can also do the previous two
                                // instructions with one. It pops bp, sr, pc
                                // in that order
.endp

You can set a local label by prefixing or postfixing it with ?.

Assembling and Linking

The assembler takes in a .asm file and outputs a .obj file. Specify the ISA version so the assembler can generate the correct code, especially if you happen to be using μ'nSP 2.0's extended instructions. The -t argument sets the version, e.g. -t4 for μ'nSP 2.0.

You can add include paths for the assembler to look through. Use the -I argument. Note that the search path starts in the same folder as the source file you're including into, then using the include paths (relative to working directory, if you're using relative paths).

For linker, most options are described in the programming tools manual. Note the following specifically:

  • -infblk: Insert a particular binary blob. This is generally used for MCUs that don't use the standard ROM header but a different one. In the case of GPL951, it uses a SPIF-specific block with some data for setting up faster SPI flash access. The data format of this blob (where it's located and its length) is described in the programming tools manual.
  • -injcks: Injects checksum for custom ROM header. Arguments:
    -injcks <checksum_start_addr> <checksum_len> <checksum_res_addr> <num_checksums> <entry_point_ptr_addr>
    
    • checksum_start_addr: address to start checksumming from
    • checksum_len: number of words to include in checksum calculation
    • checksum_res_addr: address to write checksum to
    • num_checksums: number of copies of checksum to write
    • entry_point_ptr_addr: address to write entry point to (22-bit address)
  • -at: Use automatic mode, outputting in task format (binary). Compare to -a, which is automatic mode with output specified in .ary file (or S37 by default), and -as, which is automatic mode with output in S37 format.

The body file is somewhat complex and generated by μ'nSP IDE. It tells the linker how big SRAM and ROM is, and where to place code by default.

Linker script

There are two linker scripts used: a .ary file and a .lik file. The .ary file is an automatic mode script where you could simply indicate the .obj files you want to link. When operated in automatic mode, the linker will use the .ary to generate a .lik. The .lik file specifies the objects to link, the output location and format, and where sections from each file are located. You can modify this file to pin sections to specific addresses, and the linker will retain this when regenerating the file from the .ary script.

To pin a section, uncomment its Locate line from the .lik script, and set the address after at. If you intend to move the section to somewhere else (such as from flash to RAM so you can disable flash to do something), specify an address after linkat to have the linker use that as a base for that section when you are using labels.

.map and .smy

The .map file shows a table of global symbols, info about sections within each .obj, total memory usage, and some additional information. You can use this to verify that code is where you want it to be. The .smy file is an extract of the memory summary section. This is generally printed by the IDE at the end of the build.

Init table

This is a table for initialized ram sections. It is usually processed before running _main by the startup code. The format is as follows:

struct init_table_item {
    void *dest;
    uint src_ds;        // upper 6 bits of source address
    uint src_offset;
    uint length;        // in words
};

struct init_table {
    int num_items;
    struct init_table_item items[num_items];
};

It appears a minimum of two items are reserved by the linker regardless whether there is any initialized data needed in RAM.

Stripper

The linker outputs in the usual ROM image format, but for GPL951, it uses a different ROM format which starts at 0x9000. To get a ready-to-flash image, you need to remove the first 0x9000 words from the output. The stripper program will let you cut the binary in place. I originally tried to use dd for this, but it didn't deal very well with trying to do things in-place.

Monitor protocol

The monitor program's protocol is fairly simple. It starts by setting up PWM (for driving the backlight on Punitapi-chan, which is what this was originally written for), and inits UART at 38400 baud, 8 bits, no parity, 1 stop bit. The PWM output is at 10% before UART init and 100% after UART init. This was originally mostly for debugging, since when it works you can't see the difference that quickly. The SPIF interface is also turned off by this point, with all code runnng from RAM.

All data sent and received from the monitor are in words. The monitor loops, expecting a command word. After receiving the command, it will respond with the same word so the other end can verify sending and receiving is working.

The following commands are available:

eCmd_Ping

Receives a fixed word response. The monitor responds with 0x4948.

eCmd_Read

Reads from the address space, limited to segment 0. Arguments:

  • addr: the address to start reading from
  • count: the number of words to send

The monitor will send the required number of words starting from the address. It finished by sending 0xaabb.

eCmd_Write

Writes to memory, limited to segment 0. Arguments:

  • addr: the address to start writing to
  • count: the number of words to write

The monitor reads the next count of bytes and stores them starting from the address. It finished by sending 0xccdd.

eCmd_Call

Calls a function that takes no arguments. Arguments:

  • addr: the address of the code to call, segment 0 only

The monitor responds with 0xeeff and calls the code at the address.

Invalid command

The monitor will respond with 0x474e if an invalid command is given.

Monitor host program

A host program for interfacing with the monitor can be found here.

GPL951 SPIF header observations

This is an aside regarding GPL951xxUx boot ROM behavior when used with SPI flash. For the boot ROM to pass execution to code in SPI flash, the following conditions must be met:

  • The first 32 bytes of flash consists of the string
    GP-SPIF-HEADER--0123456789abcdef
    
  • The two dwords immediately following the string are equal to each other (this is the ROM checksum, however the boot ROM does not validate it; it is left to the SPIF calibration code after it sets up the SPIFC for faster access)
  • The dword at 0x903e is a pointer to the ROM's entry point

Although there is code for setting SPIF clock and timing, it does not appear to be referenced.

About

Sample μ'nSP project from scratch, μ'nSP Monitor

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published