Getting around an openocd bug

I’ve been back working with the SAMD20 microcontroller family again for an RS485 network project. While writing code for the device I noticed that it would crash quite often. Further investigations revealed that sections of the code were not being written to flash memory. This device has a 64 byte flash page size and it will only accept writes of 64 byte blocks. If you try to write a smaller amount the write is ignored. This causes a problem when you want to write a flash image that is not an integer multiple of 64 bytes in size – the last few bytes will not be written out. The version of openocd I’m using came from here https://sourceforge.net/p/openocd/code/ci/master/tree/ . I reported the issue but while I’m waiting for a response I’ve managed to workaround the problem by adding some padding to the flash image in the linker file as shown here.

MEMORY
{
    flash : org = 0x00000000, len = 128k
    ram : org = 0x20000000, len = 16k
}
  
SECTIONS
{
        
	. = ORIGIN(flash);
        .text : {
		  *(.vectors); /* The interrupt vectors */
		  *(.text);		  
		  *(.rodata);		  

        } >flash
	. = ORIGIN(ram);
        .data : {
	  INIT_DATA_VALUES = LOADADDR(.data);
	  INIT_DATA_START = .;
	    *(.data);
	  INIT_DATA_END = .;
	  . = ALIGN(4);
        } >ram AT>flash
     
	BSS_START = .;
	.bss : {	  
	    *(.bss);
	    . = ALIGN(4);
	} > ram
	BSS_END = .;
	
	.padding : {
		  /* This is a 64 byte block of 0xff's to ensure that the last */
		  /* page of the program is written to the MCU */
		  /* The openocd SAMD driver does not seem to flush the last partial */
		  /* page out properly */

          LONG(0xffffffff);LONG(0xffffffff);LONG(0xffffffff);LONG(0xffffffff);
		  LONG(0xffffffff);LONG(0xffffffff);LONG(0xffffffff);LONG(0xffffffff);
		  LONG(0xffffffff);LONG(0xffffffff);LONG(0xffffffff);LONG(0xffffffff);
		  LONG(0xffffffff);LONG(0xffffffff);LONG(0xffffffff);LONG(0xffffffff);
	} >flash
}


This linker script is for the SAMD20E17 MCU. The value chose for the padding data is important as the erased state of flash is logic ‘1’. By using 0xffffffff as a padding value (which may or may not be written to the flash) read-back verification will report a success.

Serial transmission on the SAMD20

This is a follow up to a previous post about bare-metal programming of the SAMD20 MCU. It has been tricky. There is a very complex clock system in the SAMD20 which is difficult to set up. The UART itself is pretty straight forward however and I’ve added a simple UART transmit example to the github repository.
Shown below is a listing for the UART related and main functions. System clock setup is done in another file (init.c).
Next step: Interrupts.

/* User LED is on PA0 */
/* This example uses SERCOM0 (UART mode) to send data to a host PC
 * 
 */
#include 
#include "../samd20e16.h"
/*
 How to program: run openocd first
 Next, run arm-none-eabi-gdb in another window and execute these commands (adjust directory to suit)
 target remote :3333
 monitor at91samd chip-erase
 monitor reset halt
 monitor load_image /home/frank/Documents/programming/samd/Baremetal/samd20_examples/serial/main.elf 0
 monitor reset 
*/
void delay(uint32_t dly)
{
    while(dly--);
}

void initUART()
{    
    // UART is on:    
    PM_APBCMASK |= BIT2; // turn on SERCOM0
    // Reset SERCOM 0
    SERCOM0_CTRLA = 1; // reset the UART
    while(SERCOM0_STATUS & BIT15); // wait for sync to complete   
    while ( SERCOM0_CTRLA & BIT0 ); // wait for reset to complete
    // Will route the main 48MHz clock to SERCOM0 as the baud rate base clock.
    GCLK_CLKCTRL = 0x0d + BIT14;        
    while(  (GCLK_STATUS & BIT7)); // wait for sync to complete
    SERCOM0_BAUD = 65325; // configure for 9600 bps
    while(SERCOM0_STATUS & BIT15); // wait for sync to complete   
    SERCOM0_CTRLB = BIT17 + BIT16 + BIT9 ; // enable TX, RX, Start-of-frame wakeup, 1 stop bit, 8 bits;
    // 0x00030200 RXEN,TXEN,SFDE,
    while(SERCOM0_STATUS & BIT15); // wait for sync to complete
    SERCOM0_CTRLA = BIT30 + BIT20 + BIT2 + BIT1; // LSB first, UART mode , internal clock, PAD1 = RX, PAD0 = TX, ENABLE    
    // 0x40000006 DORD,ENABLE,MODE001 (USART INTERNAL CLOCK), ENABLE
    while(SERCOM0_STATUS & BIT15); // wait for synchronization between clock domains to complete    
    // The actual special function is selected by writing to the upper 4 bits of PAPMUX5 
    // To select function C, write  '2' to these bits
    PAPMUX4 = 0x22;    
    PAPPINCFG8 |= BIT0; // enable special function multplexing on Pin 11                
    PAPPINCFG9 |= BIT0; // enable special function multplexing on Pin 12
}
void eputc(char c)
{
    SERCOM0_DATA = c;
    while((SERCOM0_INTFLAG & BIT1)==0);// wait for TXC flag
    
}
void printString(char *String)
{
    while (*String)
    {
        eputc(*String++);
    }
}
int main()
{
    int i=0;
    PADIRSET = BIT0; // make Port A bit 0 an output
    // Startup delay to allow escape from a potentially bricked chip: ONLY DELETE WHEN SURE OF PROPER CLOCK OPERATION
    for (i=0;i<10;i++)
    {
        PAOUTTGL = BIT0; // Toggle Port A bit 0
        delay(1000000);   // Wait  
    }    
    initUART(); // get SERCOM0 going
    while(1)
    {
        printString("Hello SamD!\r\n");
        i = SERCOM0_INTFLAG;
        PAOUTTGL = BIT0; // Toggle Port A bit 0
        delay(10000);   // Wait  
        
    }
   
}