Showing posts with label QP. Show all posts
Showing posts with label QP. Show all posts

Mar 5, 2016

QPN DPP FW on Dialog DA14580

I ported the QPN-QK (QPC-nano: a bare metal, hard real-time C framework) for the Dialog's BLE chip DA14580 basic dev kit.  DA14580 has 84 KB ROM, but most of that is taken up by the bootloader and the Dialog's SDK (including the BLE functionality).  The OTP (on-time programmable) memory is only 32 KB, which is the code size limit of the Keil uVision free version.  The OTP is NOT executable; the intention is for the bootloader (in the ROM) to copy the code from OTP to (volatile) SRAM on boot or wakeup from deep sleep, during which time the volatile (vs. retained) SRAM content is lost.  The bootloader can also boot from UART, SPI flash, or I2C EEPROM, but in all cases, the code is first written to the SRAM before the real execution starts, so 32 KB of OTP is the practical code size limit.

DA1450x SDK (v 5.0.2) blinky example uVision 5 project

The SDK uses a boot_vectors.s (in peripheral_examples/shared/startup/) containing the vector table and Reset_Handler.

Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
                IMPORT  SystemInit


                LDR     R0, =SystemInit
                BLX     R0
                LDR     R0, =__main
                BX      R0
                ENDP


SystemInit() is a CPU/board specific function, supplied in system_ARMCM0.c (right next to the voot_vectors.s).  For the peripheral example, it uses the internal 16 MHz crystal oscillator.

void SystemInit (void)
{
  SetWord16(TIMER0_CTRL_REG,0x6);      // stop timer
  NVIC_DisableIRQ(SWTIM_IRQn);     // disable software timer interrupt
   
  if ((GetWord16(CLK_CTRL_REG) & RUNNING_AT_XTAL16M) == 0)
  {
    while( (GetWord16(SYS_STAT_REG) & XTAL16_SETTLED) == 0 );

    SetBits16(CLK_CTRL_REG , SYS_CLK_SEL ,0);
    while( (GetWord16(CLK_CTRL_REG) & RUNNING_AT_XTAL16M) == 0 ); // wait for actual switch
  }
}


__main() is not supplied in the SDK, but it's probably just a C runtime init and then a jump to the application main.

Memory map: use case 23

The boot_vectors.s also hardcodes the stack and the heap size:

Stack_Size      EQU     0x00000200

Heap_Size       EQU     0x00000100

The scatter file (equivalent to the GNU ld file) also hard codes the stack size.

LR_IROM1 0x20000000 0x00009800  {
...
    RW_IRAM1 (0x20009800 - 0x200) UNINIT 0x200 {         ; Stack
        .ANY (STACK)
    }

}

The system memory starting and ending address is for the memory map case 23, among the memory map choices listed in DA1258x SDK reference manual (Dialog document UM-B-051).  The blinky example puts everything (code, data, bss) in the 38 KB non-retention RAMP.

Debugger init

When debugging, the debugger ini file should match this starting address, as in this example:

RESET
E long 0x50000012 = 0xa4
E long 0x50003308 = 0x2e
LOAD %L
SP = _RDWORD (0x20000000)
$ = _RDWORD (0x20000004)


Let's briefly examine the system register settings 0x00A4 and 0x002E above.  SYS_CTRL_REG is configured for:
  • DEBUGGER_ENABLE
  • PAD_LATCH_EN
  • RET_SYSRAM: in development mode, system is NOT actually powered off (so that SysRAM is retained), and .data is NOT copied to SysRAM when the uC wakes up.
GP_CONTROL_REG = 0x2E = 0x17 << 1 + 1 specifies:
  • EM_MAP = 0x17 = case 23
  • BLE_WAKEUP_REQ: BLE wakes up

System init

To understand the system_init() called by main()--what kind of initialization does the example do?--, I need the Registers section of the DA14580 datasheet.

void system_init(void)
{
    SetWord16(CLK_AMBA_REG, 0x00);                 // set clocks (hclk and pclk ) 16MHz
    SetWord16(SET_FREEZE_REG,FRZ_WDOG);            // stop watch dog   
    SetBits16(SYS_CTRL_REG,PAD_LATCH_EN,1);        // open pads
    SetBits16(SYS_CTRL_REG,DEBUGGER_ENABLE,1);     // open debugger
    SetBits16(PMU_CTRL_REG, PERIPH_SLEEP,0);

}

Annotation of the above code:
  • APB (peripheral bus) and AHB (high speed bus) runs at the same frequency as the source (16 MHz above).
  • Freeze the watchdog HW.
  • Latch is transparent, pad can be retained (control signal is NOT retained)??
  • Retain SWD IO/CLK pins for debugging (instead of using them as GPIO).
  • Supply power to peripheral.  You can turn off the radio in the same PMU_CTRL_REG.
periph_init() repeats the above setup exactly anyway, so system_init() is technically unnecessary.  Once the entire peripheral block is powered up, the uC will get past this:

    while (!(GetWord16(SYS_STAT_REG) & PER_IS_UP));

Even though the peripheral block itself is now powered, clocks to individual HW (e.g. UART) must be turned on separately through the CLK_PER_REG.  The example blinks the GPIO LED, so that GPIO port is configured for output

    GPIO_ConfigurePin(LED_PORT, LED_PIN, OUTPUT, PID_GPIO, false);

Since the blinky example writes to the serial console, UART2 pins are configured.

    GPIO_ConfigurePin(UART2_GPIO_PORT, UART2_TX_PIN, OUTPUT, PID_UART2_TX, false);
    GPIO_ConfigurePin(UART2_GPIO_PORT, UART2_RX_PIN, INPUT, PID_UART2_RX, false);
    SetBits16(CLK_PER_REG, UART2_ENABLE, 1); // enable  clock for UART 2
    uart2_init(UART2_BAUDRATE, UART2_DATALENGTH);

Booting the FW from UART

During development, it is convenient to download and run (and debug) the FW from an IDE such as the Keil uVision.  But to use DA1458x as a slave uC in a production setting, a boot master needs to hold the FW and feed it to DA1458x during boot, according to the Dialog application note AN-B-001: DA1458x Booting from serial interface.  The boot code on the OTP checks all 1458x's serial peripherals sequentially, in the following steps:
DA1458x is acting as a slave device in steps 1 through 6, and as a master device for the rest of the steps.  Checking for an external master only happens once during reset, but the boot code loops through steps 7 and on 5 times before giving up and spinning, waiting for a SWD master intervention.  According to the above steps, the earliest step where an external UART master can boot the DA1458x is step 3, but for the fastest UART baud rate (8 bit, no parity in all cases) I must use step 4 (P0.2/3), as you can see in the following table:
The message protocol for transferring the FW image from the UART master to DA1458x is in the DA1458x connected to UART section of the same application note.  Dialog's own SmartSnippets GUI uses this protocol to download FW to DA1458x.

In a production device, where the boot device/pin is fixed, checking an external SPI master lengthens the boot latency.  Another Dialog document UM-B-012 DA14580/581/583 Creation of a secondary bootloader explains modifying the boot code to skip the unnecessary checks.

DPP (dining philosopher problem) solved with QPN on DA14580

The QK port for arm-cm discriminates itself for M0/M1 (which has no preemption feature) and M3/4/7.  I copied the example dpp_nucleo_l053r8 and modified only the bsp.c

In the CMSIS SystemInit(), I chose to use the internal 16 Mhz clock and power up the peripheral domain power.

void SystemInit (void)
{
    SetWord16(TIMER0_CTRL_REG,0x6);      // stop timer
    NVIC_DisableIRQ(SWTIM_IRQn);     // disable software timer interrupt
   
    if ((GetWord16(CLK_CTRL_REG) & RUNNING_AT_XTAL16M) == 0)
    {
        while( (GetWord16(SYS_STAT_REG) & XTAL16_SETTLED) == 0 );     // wait for XTAL16 settle
        SetBits16(CLK_CTRL_REG , SYS_CLK_SEL ,0);                     // switch to XTAL16
        while( (GetWord16(CLK_CTRL_REG) & RUNNING_AT_XTAL16M) == 0 ); // wait for actual switch
    }
    //SetWord16(GP_CONTROL_REG, 1 << 1 | 0);//Case 1 in DA1458x SDK ref Appendix A.1

    // system init
    SetWord16(CLK_AMBA_REG, 0x00);                 // set clocks (hclk and pclk ) 16MHz
    SetWord16(SET_FREEZE_REG,FRZ_WDOG);            // stop watch dog   
    SetBits16(SYS_CTRL_REG,PAD_LATCH_EN,1);        // open pads
    SetBits16(SYS_CTRL_REG,DEBUGGER_ENABLE,1);     // open debugger
    //SetBits16(PMU_CTRL_REG, PERIPH_SLEEP,0);     // exit peripheral power down

    // Power up peripherals' power domain
    //Q: Why set the PMU_CTRL_REG again with same value?
    SetBits16(PMU_CTRL_REG, PERIPH_SLEEP, 0);
}


The DA14580devkB board has only 1 user LED, so I used it for the philosopher[0] stat.

void BSP_displayPhilStat(uint8_t n, char const *stat) {
    if (stat[0] == 'h') {
        GPIO_SetActive( LED_PORT, LED_PIN);/* turn LED on  */
    }
    else {
        GPIO_SetInactive(LED_PORT, LED_PIN);/* turn LED off */
    }
}


The DPP example uses systick interrupt, but a typical low power FW will use a tick-less timer instead, rendering the current systick related code unnecessary in QF_onStartup():

void QF_onStartup(void) {
    /* set up the SysTick timer to fire at BSP_TICKS_PER_SEC rate */
    SetBits32(&SysTick->CTRL, SysTick_CTRL_ENABLE_Msk, 0);          // disable systick
    SetBits32(&SysTick->LOAD, SysTick_LOAD_RELOAD_Msk, BSP_TICKS_PER_SEC-1); // set systick timeout based on 1MHz clock
    SetBits32(&SysTick->VAL,  SysTick_VAL_CURRENT_Msk, 0);          // clear the Current Value Register and the COUNTFLAG to 0
    SetBits32(&SysTick->CTRL, SysTick_CTRL_TICKINT_Msk, 1); // generate interrupt
    SetBits32(&SysTick->CTRL, SysTick_CTRL_CLKSOURCE_Msk, 0);// use a reference clock (1 MHz)
    SetBits32(&SysTick->CTRL, SysTick_CTRL_ENABLE_Msk, 1);          // enable systick

    /* set priorities of ALL ISRs used in the system, see NOTE00
    *
    * !!!!!!!!!!!!!!!!!!!!!!!!!!!! CAUTION !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    * Assign a priority to EVERY ISR explicitly by calling NVIC_SetPriority().
    * DO NOT LEAVE THE ISR PRIORITIES AT THE DEFAULT VALUE!
    */
    NVIC_SetPriority(SysTick_IRQn,   SYSTICK_PRIO);
    /* ... */

    /* enable IRQs... */
}


The DPP project structure looks like this:
Besides the board specific code in bsp.c (discussed above),  DA14580devkB folder contains the board specific files.

The uVision project generates afx file (equivalent to ELF file from gnu toolchain), but to generate a binary file (for booting the target from another uC for example), I added a post-build step in the User tab of the project option:

fromelf --bin --output .\out\app_uart.bin .\out\dpp_qk.axf

Unlike the example blinky project, QPN projects do NOT use heap space, and I specified that as a #define into the assembler (which assembles the boot_vectors.s)
The app FW bin (app_uart.bin) produced with these changes is 6.5 KB (which should contain everything that will be in the RAM, except the .bss section).  When I download the binary from uVision to the target over USB J-Link connection (DA14580devkB feature), I see the philosopher hungry state go on and off on the green user LED--as expected.

Adding QS real-time software tracing to QPN

When Miro Samek created QPN from QPC, he left out QSpy tracing feature of the QPC to keep the memory footprint down.  The QSpy sources are nicely refactored out in QPC:
  • include/
    • qs.h
  • source
    • qs.c
    • qs_fp.c
    • qs_pkg.h
    • qs_rx.c: receive and parse command from the QSpy viewer.  Unnecessary for passive tracing.
    • qstamp.c: build date.
I had to define the QSignal and QEvtSize data type sizes in qs_port.h, because QPN hard codes those types and QS cannot figure it out on its own.

#define Q_SIGNAL_SIZE 1
#define QF_EVENT_SIZ_SIZE 1
#define QF_CRIT_ENTRY(dummy) QF_INT_DISABLE()
#define QF_CRIT_EXIT(dummy)  QF_INT_ENABLE()


Also, on Cortex M0, there is no preemption of the ISR, so it's OK to just lock out the whole interrupt.  To use Q_SPY, I have to define Q_SPY to the CPP.
Now the app FW bin size is 9.4 KB, and the total size on RAM < 12 KB.

Appendix: More memory for the application FW

Since I am not interested in using DA14580's BLE HW, I can grab all memory that was reserved for BLE in the blinky example.  Unfortunately, there is no memory map configuration WITHOUT any memory for the BLE.  The best I can do is use case 1.
The starting address is still 0x20000000, but the end address is now 0x2000C000.  Whether I actually retain the top 6 KB is up to me (it does NOT retain out of reset).  The linker script (scatter file) for this modified memory map reserves only 1 KB for the stack, and removes heap altogether.

LR_IROM1 0x20000000 0xC0000  {
    ER_IROM1 0x20000000 0xB000  {
        *.o (RESET, +First)
        *(InRoot$$Sections)
        .ANY (+RO)
        .ANY (+RW)
    }
    ER_IROM2 +0  {
     .ANY (+ZI)
    }
    RW_IRAM1 (0x2000C000 - 0x1000) UNINIT 0x1000 { ; Stack
        .ANY (STACK)
    }
}

The uC's memory map is changed during runtime by setting GP_CONTROL_REG (0x50003308) to (1 << 1) + 0 = 2; the 0 implies BLE_WAKEUP_REQ is turned off, like this:

    SetWord16(GP_CONTROL_REG, 1 << 1 | 0);
This should be done in SystemInit (which is called even before the C runtime initializes), as soon as the oscillator stabilizes.

Hard faulting as soon as I start!

Dec 6, 2015

Meet the Cypress PSoC 4 BLE

WARNING: unfinished blog entry

About half year ago, I went to a seminar hosted by Arrow, where I received this nice development board, and interacted with the Bluetooth feature of the board.
The red baseboard is the generic development motherboard with peripherals (e.g. capacitive touch sensor array) and USB based emulator connection to the PC, and the smaller red daughter board is the CY8C427LQI0BL483 eval board.  The black board is the "BLE dongle" that runs the BLE host emulation FW, for the PC to act as the counter part (usually through the CySmart SW) of the red eval board, like this picture:

Until now, my only ARM experience has been on TI Stellaris (now called Tiva) which is ARM Cortex-M4 based, and  the Zedboard, which packs a dual core ARM Cortex-A9.  But the BLE Pioneer Kit runs on Cortex-M0, so I began to check out the low level details of the processor, by reading through my favorite real-time OS (of a sort): QP, which has an example port (of QP version 4.5.04) to ARM Cortex M0 on the LPCXpresso-1114 board.  In this blog entry, I port QP to the CY8CKIT-042-BLE, and implement Bluetooth "Find Me" device in QP.

Getting started with the PSoC creator--on virtualbox

Setting up PSoC Creator on virtualbox

Similar to the Xilinx Zynq, PSoC is is programmable logic device with a CPU on the same die.  Being configurable means that the SW runs on top of customized HW--even if it is one that has been stable for some time.  So the PSoC Creator's is like the Xilinx Vivado IDE: it creates/configures the HW.  It is a convenient IDE that runs only on Windows, so I need to run in a virtual machine.  Installing the whole (kitchen sink) PSoC BLE toolchain is easy, but takes a long time.  The biggest problem with developing an embedded target in a virtual machine is getting the virtualbox guest to see the development board's USB endpoints.   It took me half a day to figure out the following steps (reinstalling the virtualbox several times along the way): I (the Linux user running the virtualbox) must be in the "vboxuser" and "lp" group:

sudo usermod -aG vboxuser henry
sudo usermod -aG lp henry

Then I had to RESTART the PC, before I could see in the virtualbox USB settings window the following USB devices the on the pioneer baseboard:
If the toolchain has been correctly installed, the Windows device drives will be found automatically when the Windows virtual machine boots up the next time, as you can see here:
These devices should show up in the Windows Device Manager (Start --> right click on Computer --> Manage --> Device Manager):
  • Bluetooth Radios
  • Network adapters: PAN and RFCOMM Protocol TDI
  • Ports: KitProg USB-UART
  • USB controllers: KitProg
When I plug in the "BLE dongle", I get another KitProg USB-UART device (enumerated on COM4)), which the CySmart SW can automatically find, as you can see here:

Blinking an LED

The project will drive an LED with a PWM HW, available from the Cypress Component Catalog tab --> Digital --> Functions --> TCPWM mode (I could also search for this in the textbox).  Supply the clock, and drive a digital out pin from line_n as shown below:
Change the clock frequency to 1 kHz, and then rename the output pin to Red_LED.

Optionally, I can draw the rest of the LED circuit elements on the schematic, as shown below):
These off-chip components are available in the "Off-Chip" catalog tab, and do not have any down stream consequences for the PSoC creator.  [Q: what does "include in Netlist" do?]
To constrain the output pin to an actual pin on the chip, bring up the design wide resource wizard in the project explorer.  Remember that the PWM HW is INSIDE the chip, and constrain the Red_LED pin to P2_6 (pin 43) from the drop-down selection, to satisfy the actual Pioneer base board schematic for the 3-color LED shown below:
Generate the HW through menu --> Build --> Generate Application.  In PSoC Creator 3.x there is a code analysis tool bug that causes crash.  The workaround is to remove ("replace in file" feature) the following string from ALL files:

/* [] END OF FILE */

The menu item name IS appropriate because this generates C code for the HW, similar to how Xilinx BSP generates C stub code for each peripheral.  Since I prefer writing bare metal C code, I don't like this option.  Perhaps I can turn off the code generation in the future in the build settings:

At any rate, the main() simply calls PWM_Start() and loops forever:

#include <project.h>

int main()
{
    PWM_Start();//CyGlobalIntEnable; /* Enable global interrupts. */

    /* Place your initialization/startup code here (e.g. MyInst_Start()) */

    for(;;)
    {
        /* Place your application code here. */
    }
}

When I build and program this to the target, the red LED is linking at 2 Hz, as expected (the clock frequency is 1 kHz, and the PWM period is 500).  So what just happened?

The auto-generated code PWM_Start() is ultimately writing to the register of course.  For example, the very first code run in PWM_Start() is resetting the PWM HW's control register to the default values:

    #if (PWM__PWM_SEL == PWM_CONFIG)
        PWM_CONTROL_REG = PWM_CTRL_PWM_BASE_CONFIG;

This PWM_CONTROL_REG is defined in PWM.h, cyfitter.h, and cydevice_trm.h: PWM_CONTROL_REG -->  PWM_cy_m0s8_tcpwm_1__CTRL pointer --> CYREG_TCPWM_CNT0_CTRL --> 0x40200100u

Note that the same cydevice_trm.h which defines the CYDEV_TCPWM_BASE and all other register definitions, and is also auto-generated.  The "trm" in the header file name is of course referring to the PSoC TRMs (the technical reference manuals), which are available here, where I downloaded 3 documents:
  • PSoC 4 BLE Architecture TRM 
  • PSoC 4 BLE Registers TRM.pdf 
  • CY8C41XX, CY8C42XX Programming Specifications.pdf 
As a proof that I am reading the right manuals for the part, the PSoC 4 BLE Register TRM section 6.1.15, TCPWM_CNT1_CTRL register's address is 0x40200140.

While everything seems to work, this is not a good FW demo, because the HW (clock and PWM) does all the work.  Production FW needs system tick and multiple threads, which any real-time OS supplies.

Understanding the PSoC BSP (auto-generated code)

Just like the auto-generated BSP code for the Zynq platform, this PSoC BSP code is an excellent way to learn the FW-HW interface for a given PSoC HW, by drilling down into source in PSoC Creator IDE (Ctrl F12).  But the most direct way to get at the boot code is from the linker script, which is in the auto-generated cy_boot/cm0gcc.ld, which specifies the first instruction to execute in a program:

ENTRY(Reset)
SEARCH_DIR(.)
GROUP(-lgcc -lc -lnosys)

Like the files listed in INPUT, the libraries listed in GROUP are linked into the linker target, so the executable will be pulling some initialization routines in libc.  The linker file also specifies the stack and heap locations:

MEMORY
{
  rom (rx) : ORIGIN = 0x0, LENGTH = 131072
  ram (rwx) : ORIGIN = 0x20000000, LENGTH = 16384
}

PROVIDE(__cy_stack = ORIGIN(ram) + LENGTH(ram));
PROVIDE(__cy_heap_end = __cy_stack - 0x0800);

In this case, half of the 16 KB RAM is given to the stack.  The reset code gets placed after the ISRs:

SECTIONS
{
  /* The bootloader location */
  .cybootloader 0x0 : { KEEP(*(.cybootloader)) } >rom
...
  .text appl_start :
  {
    CREATE_OBJECT_SYMBOLS
    PROVIDE(__cy_interrupt_vector = RomVectors);

    *(.romvectors)

    /* Make sure we pulled in an interrupt vector.  */
    ASSERT (. != __cy_interrupt_vector, "No interrupt vector");

    ASSERT (CY_APPL_ORIGIN ? (SIZEOF(.cybootloader) <= CY_APPL_ORIGIN) : 1, "Wrong image location");

    PROVIDE(__cy_reset = Reset);
    *(.text.Reset)
    /* Make sure we pulled in some reset code.  */
    ASSERT (. != __cy_reset, "No reset code");
...

As in any firmware, after PoR, PSoC 4 firmware needs to initialize many configuration registers before the main FW code can run properly; the clock and peripheral initialization is the best such example.  As explained in AN60616 PSoC ® 3 and PSoC 5LP Startup Procedure, the auto-generated cyfitter function performs a lot of such initialization.  For PSoC Creator 3.2 autogenerated code (for PSoC 4 BLE at least), that happens in initialize_psoc() function, in Cm0Start.c.  The confusing part is that the entry point of the auto-generated code for GCC toolchain is NOT initialize_psoc(), but rather Reset() vector--which is in the same file--that does NOT call initialize_psoc function explicitly, but calls Start_c(), which will in turn call the application main, as shown in the code snippets from Cm0Start.c below:

    CY_SECTION(".romvectors")

    const cyisraddress RomVectors[CY_NUM_ROM_VECTORS] =
{
    INITIAL_STACK_POINTER,   /* The initial stack pointer  0 */
    (cyisraddress)&Reset,
    &IntDefaultHandler,      /* The NMI handler            2 */
    &IntDefaultHandler,      /* The hard fault handler     3 */
};

void Reset(void)
{
#if (CYDEV_PROJ_TYPE == CYDEV_PROJ_TYPE_LOADABLE || CYDEV_PROJ_TYPE == CYDEV_PROJ_TYPE_LOADABLEANDBOOTLOADER)
        __asm volatile ("MSR msp, %0\n" : : "r" ((uint32)&__cy_stack) : "sp");
    #endif /* CYDEV_PROJ_TYPE_LOADABLE */
...


    Start_c();
}

Thus, we have a situation where the FW application seems to be called BEFORE the configuration registers are initialized--which cannot be right.  In contrast, other toolchains (like IAR) have an explicit call path to initialize_psoc() function.

__attribute__ ((constructor(101)))
void initialize_psoc(void) {
  CY_CPUSS_CONFIG_REG &= (uint32) ~CY_CPUSS_CONFIG_VECT_IN_RAM;
  for (indexInit = 0u; indexInit < CY_NUM_VECTORS; indexInit++) {
    CyRamVectors[indexInit] = (indexInit < CY_NUM_ROM_VECTORS) ?
      RomVectors[indexInit] : &IntDefaultHandler;
  }
  cyfitter_cfg(); /* Initialize configuration registers. */
  cySysNoInitDataValid = 0u;
  ...
  CY_CPUSS_CONFIG_REG |= CY_CPUSS_CONFIG_VECT_IN_RAM;
}

For the GNU toolchain case, initialize_psoc() IS being called IMPLICITLY, because initialize_psoc() has the "constructor(101)" attribute, which is explained in this link (in paraphrased summary, the function is put into the .ctor section of the text).   Start_c() (called from Reset vector, as stated earlier) will in turn call __libc_init_array(), which is supplied in libc.  Inside, it will call all static constructors--which are in the .ctor section of the text.  Therefore, initialize_psoc() will be called implicitly right before the application main() is called, as shown in this stack trace:

0 initialize_psoc() .\Generated_Source\PSoC4\Cm0Start.c 509 0x0000026A (All) 
1 __libc_init_array() ?????? ?????? 0x000006AA (All) 
2 Start_c() .\Generated_Source\PSoC4\Cm0Start.c 346 0x00000258 (All) 
3 Reset(int nbytes = <optimized out>) .\Generated_Source\PSoC4\Cm0Start.c 390 0x00000016 (All) 
...

Resuming the discussion about the initialize_psoc() action itself, note that the last line tells the PSoC chip that the vector table has now been relocated to the SRAM.

OTA (over the air) FW update

Field upgradeability of a FW is critical because it takes a long time for the SW to mature to match the sophistication of typical HW.  To replace a FW, one of course needs another FW (called bootloader) that will do that job, and on many chips (like MSP430), the bootloader is burned into ROM.  But on  PSoC, even the bootloader is on the flash, and a bootloader project is created just like a regular FW--with configurable HW and FW features.  The only difference is that a bootloader project has a top level bootloader "component" in the TopDesign.cysch (the counterpart FW to a bootloader--a bootloadable FW--has the top level bootloadable component in TopDesign.cysch) as in the snapshot of the Cypress BLE OTA example project (from the 100 projects in 100 days website):
Note that there are only a few non-auto-generated files for the project:
  • TopDesign.cysch
  • BLE_External_Memory_Bootloader.cydwr (design wide resource)
  • Header/source files
    • CustomInterface.[ch]
    • debug.[ch]
    • Encryption.[ch]
    • ExternalMemoryInterface.[ch]
    • Options.h
    • main.c
    • WriteUserSFlash.[ch] 
Options.h just has a few #defines that control the bootloader FW features, which currently seem to be the same for both the debug and release mode:

#define ENCRYPT_ENABLED         (NO)
#define DEBUG_UART_ENABLED      (NO)
#define CI_PACKET_CHECKSUM_CRC  (NO)
#define KEY_ROW_NUM             (0u)

#define ENCRYPTION_ENABLED      (ENCRYPT_ENABLED || CYDEV_BOOTLOADER_ENABLE)

I2C 2.0 (now there is v 3.0 available) component is from the component catalog, and has been configured as the master, at 1000 KHz (actual 941 KHz).  It is used to control the FMEM that happens to be on the pioneer kit board (U3), to download the new (bootloadable) FW to.  SW transmit UART (makes sense to avoid eating up the precious serial HW--since there are only 2 on this chip) component is version 1.30 (now there is v 1.40), and configured for 115200 baud rate and static pin assignment.  This component is only necessary to print debug messages to the development host serial terminal.  The I2C SCL/SDA pins and UART TX pins are constrained in the "Pins" tab of the design wider resource screen, as you can see below. 
The Clocks tab of the design wide resource shows that the I2C clock is HFCLK/2 = 16 MHz nominal, since the HFCLK ticks at 32 MHz in this design.

Interrupts tab shows there are 2 unassigned interrupts in this design: EMI_I2CM_SCB_IRQ, and BLE_bless_isr.  System tab shows that the heap and stack are 512 B and 2 KB, respectively.

A bootloader FW reset vector is similar to a normal FW studied above, except for a check before C startup:

    #if (CYDEV_BOOTLOADER_ENABLE)
        CyBtldr_CheckLaunch();
    #endif /* CYDEV_BOOTLOADER_ENABLE */
    Start_c();

CyBtldr_CheckLaunch (in Generated_Source\Bootloader\Bootloader.c) checks whether to run the application or the bootloader--only if soft reset--using the Bootloader_runtype global variable.  If hard reset or there is no loadable app, the boot loader will continue onto Start_c(), then to initialize_psoc as discussed above, and finally the application main, which starts the SW UART TX debug print library:

    #if (DEBUG_UART_ENABLED == YES)
        UART_Start();
    #endif /* (DEBUG_UART_ENABLED == YES) */

    DBG_PRINT_TEXT("\r\n");
...

FreeRTOS on PSoC 4

FreeRTOS is the most popular real-time OS--because of its feature/cost ratio.  Cypress already ported FreeRTOS 8 to PSoC 4 (pioneer kit) for both the ARM (MDK) and GNU compiler a year and a half ago.  Let's give the gcc version of the demo a whirl.  The download contains the FreeRTOS 8.0.0 source, and 2 examples for the cy8ckit pioneer board: Demo.cydsn and NewDesign.cydsn, contained in a workspace file cy9ckit-042-gcc.cywrk.  Both projects pull in the included FreeRTOS source just discussed, as you can see in the screenshot of the NewDesign project:
The header files in these folders are pulled in through the compiler include path additions:
  • ..\..\FreeRTOS\Source\include
  • ..\..\FreeRTOS\Source\portable\GCC\ARM_CM0
The linker file is similar to the blink example I just went through, except for the difference in ROM, RAM, stack, and heap sizes:
  • ROM: 128 KB vs. 32 KB for NewDesign example
  • RAM: 16 KB vs. 4 KB for NewDesign example
  • Heap: 64 B vs. 0 B for NewDesign example
  • Stack: 2 KB vs. 120 B for NewDesign example
It seems strange that FreeRTOS, which provides malloc() would use so little heap.  The FreeRTOSConfig.h shows the heap as 3000 B:

    #define configMAX_PRIORITIES ( 4 )
    #define configTOTAL_HEAP_SIZE ( 3000 )

But it is corroborated by the "Design Wide Resource" page:
This linker file will then invoke the Reset handler just like the case I just went through, which calls into main(), where FreeRTOS setup happens:

There must be something else going on?


 I think the firmware can be improved as an explicit state machine.  A more interesting way to blink the LED is with a state machine driven by a HW timer.

An HSM (hierarchical state machine) based PSoC 4 FW architecture 

Understanding the QP ARM Cortex M0 port


Making the HW Bluetooth capable

Configuring the PSoC Bluetooth HW for the "Find Me" target profile

Now I am ready to reattempt the BLE Lab #1: implementing a smart keychain device, controlled by a Bluetooth client on the PC (I will change the remote attribute of the target through the CySmart SW):
To add some excitement to the otherwise boring activity of changing the alert level of a Bluetooth peripheral, the peripheral displays a different light pattern.

In PSoC Creator, create a new project for the PSoC 4100 BLE / PSoC 4200 BLE Design template, but use an empty schematic, as shown below:
The project must target a specific device, shown in the Project --> Device Selector window.  The choice is easy for the eval board: CY8C4247LQI-BL483.  The peripherals already packed into the die are interesting.

To create a new HW, the drag-and-drop the desired components onto the schematic, as shown below:
I would guess that all Bluetooth configurations are SW modifiable at a later time, but for a simple exercise, the configurations are specified while configuring the HW (double click the component).  The BT peripheral gets the "Find Me" standard profile collection setting, showing the profile and GAP roles:
The next tab (Profiles), shows the BT-Sig defined attributes for the chosen profile, but this is NOT where I edit these attributes; I do that in the next tab: GAP Settings.
BT device is highly configurable.  In "Advertisement settings", I turn off timeout on the fast advertising interval and "Slow advertising interval".  The advertisement packet itself is also configurable; I turned on the Service UUID --> Immediate Alert.  When a collector scans the target, the target responds with the packet defined in the "Scan response packet definition: Local Name and Appearance in this case.  Looking at nodes like "Peripheral preferred connection interval", I understand that BT expertise may have less to do with understanding the BT API (which is rather simple) but knowing as many of these possibly esoteric attributes.  The whole seminar skipped security discussion altogether and used the following insecure setting:
The BT peripheral is now configured.

Mar 31, 2015

State machine based Qt5 GUI on Zedboard

In a previous blog entry, I explored creating a minimal embedded Linux distribution containing the Qt5 framework, and writing and debugging a "Hello world" Qt GUI application.  Whenever possible, I write all my SW within an event-driven, hierarchical state machine framework called QP.  But since Qt is also an event-driven framwork in its own right, meshing the 2 together is not straight-forward.  When creating a WPF MVVM (model-view-view model) GUI application with state machines, I could update the WPF view model from a special active object (I called it the GuiStateMachine) in response to any update events (of interest to the GUI) from ALL other active objects.  Apparently, you cannot do that in Qt, because in the official Qt-QP integration example, the singleton GUI state machine runs in Qt context.  So unlike in my WPF-QP integration, the events delivered to the GUI state machine (active object, really) are transformed into a Qt event and shoved into the Qt's event delivery mechanism.  The Qt-QP reference application is available for mingw, but I cross-compile for the Zynq (ARM Cortex A9), so I am going to modify the reference application for my situation.

Create the DPP Qt Widgets project

The reference application creates the QP Qt library first.  But on my system, one Qt GUI is the only application (I am an embedded SW engineer, not a desktop SW engineer), so I will not bother with a separate library, and just put all code in 1 Qt widgets application, in the qpcpp/example/qt/arm/buildroot folder.

~/work/Dorking/QP/qpcpp/examples/qt$ mkdir -p arm/buildroot

Then in Qt Creator (the previous blog entry discussed how to get and install the Qt Creator FROM qt.io rather than as a Debian package)
  1. Click "New Project" button, and then choose the "Qt Widgets Application" template.
  2. Following the reference application example, I create a project called "dpp-gui" in the /mnt/work/Dorking/QP/qpcpp/examples/qt/arm/buildroot folder just created.
  3. Next, I choose the zedbr2 kit I created in the  previous blog entry.
  4. In a departure from the example, I create my GUI as a QMainWindow (vs. QDialog).  Also unlike the example, I WILL use the form.  But I will still call the main class "Gui", to follow the example.
Qt Creator can ready build this empty main class, which is always a good first step.

Preprocessor include path and defines in qmake project file

At minimum, the project must include the QP include/, qep/source/, qf/source/, and  the QP port folders.  Unlike other IDEs, the build variables like include paths are NOT a project property; I write these are directly into the project (.pro) file in a text editor, using a qmake variable, like this:

QP_ROOT = ../../../../..

INCLUDEPATH += $$QP_ROOT/include $$QP_ROOT/qep/source \$$QP_ROOT/qep/source \ $$QP_ROOT/qf/source \$$QP_ROOT/qf/source \ $$QP_ROOT/ports/qt




Qt itself has a state machine infrastructure, which is redundant for a QP state machine application, so I turn off the Qt's state machine feature in the qmake .pro file:

DEFINES += QT_NO_STATEMACHINE

Add sources to the project and tailor to my needs

QP platform independent sources

In Qt Creator, right click on Sources --> Add Existing Directory --> Browse to the qpcpp/qep/source/ folder --> Start Parsing, to expand the folder and unselect the unnecessary files, as shown below (I do not use FSM, only HSM):
I later learned that you can also include the header files, and Qt Creator will correctly pull them into the HEADERS variable, so qep_pkg.h should have been checked in the above screenshot.

I add qpcpp/qf/source folder similarly, without leaving out any files this time.

Note on updating to the QP 5 API

When copying examples written for QP API 4.5 or earlier, the following changes are required:
  • Delete the deprecated call to QS_RESET()
  • QTimeEvt ctor now takes the owning active object as the 1st argument.  In C++, that would show up as the "this" pointer if the timer belongs to an active object.  In exchange, the armX method of the QTimerEvt--which should be used instead of postIn() method--now does NOT take an active object.
  • Q_NEW now takes ctor arguments, to call the PLACEMENT new operator (i.e. unlike the new does NOT hit the heap) of the type being created.  While this is great for a single process usage of the memory pool, the virtual table you get with the new operator is dangerous when the memory pool spans multiple processes (through shared memory)--as will be the case for me.  The danger lies in the possibility for different compiler versions laying out the virtual table differently (C++ compilers are notorious for this, even among different versions).  I decide to play it safe here, turn off QEvent's CTOR and VIRTUAL features in qep_port.h, as shown below (and pay the price of having to initialize the memory pool objects myself):
// don't define QEvent to avoid conflict with Qt
#define Q_NQEVENT    1

// provide QEvt constructors
#undef Q_EVT_CTOR

// provide QEvt virtual destructor
#undef Q_EVT_VIRTUAL

QP Qt port sources

Because Qt is a multi-platform code, the example QP port to mingw Qt still works for embedded ARM.  I just have to include the qpcpp/ports/qt/ folder, like I have done for the qep/ and qf/ folders above.  But since the PixelLabel is only necessary for the fly-and-shoot example, I excluded them.

SOURCES += \...
$$QP_ROOT/ports/qt/guiapp.cpp \
$$QP_ROOT/ports/qt/qf_port.cpp


HEADERS += gui.h \
$$QP_ROOT/ports/qt/qep_port.h \
$$QP_ROOT/ports/qt/qf_port.h \
$$QP_ROOT/ports/qt/tickerthread.h \
$$QP_ROOT/ports/qt/aothread.h \
$$QP_ROOT/ports/qt/guiapp.h \
$$QP_ROOT/ports/qt/guiactive.h

Unlike the example Qt integration on mingw, setting a stack size to 4 KB is preventing QThread start, so I commented them out and let QThread use the default thread stack size for now.

   //thread->setStackSize(stkSize);

Application support files

The final step in mating QP to an application is to specify functions that QP calls for certain events (startup, onClockTick, onAssert, etc) and the application state machine calls (like updating the philosopher stats from the Table state machine).  Unlike the port files, which can theoretically be shared between different QP-Qt projects (again, I will only have 1), the application specific files are coupled to the application logic.  For the DPP application, dpp.h and the bsp header/source files are such files, so I add them to the first lines of SOURCES and HEADERS in the qmake pro file:

SOURCES += main.cpp gui.cpp bsp.cpp philo.cpp table.cpp \
...


HEADERS += gui.h bsp.h dpp.h \
...



dpp.h contains the application specific event class TableEvt.  To turn off the event polymorphism feature, I take in only the signal number in the TableEvt constructor.

When I examine bsp.cpp, I see that the philosopher states (THINKING/HUNGRY/EATING) are displayed with QPixmaps showing 3 different PNG files, and the table state (PAUSED/SERVING) is displayed with a text on a button.  The images for the philosopher states are in res folder,  pointed to by the gui.qrc (Qt resource) file.  So I add this file to the project (Add Existing File).  I also copied the entire res/ folder from the mingw example folder, so that when I click on one of the PNG files in the resource, I see the image in the Qt Creator, like this:

In the qmake pro file, the resource shows up like this:

RESOURCES += gui.qrc

To update the files to the latest QP API, I make the changes discussed above, in "Note on updating to the QP 5 API" section.

UI

Instead of just blindly copying the QDialog based UI from the example, I went through the trouble of copying the buttons and labels from the example UI to the QMainWindow based UI, all to preserve the possibility of using the top menu and the bottom status bars in the future.  In Qt Creator's Designer View, the UI looks like this:
Note that all widgets I copied are in the central widget; that is, the north, south, east, west widget areas do not exist.

I wire the signals emitted from the widgets to the 3 slots defined in gui.cpp constructor:

...
    QObject::connect(m_quitButton, SIGNAL(clicked()), this, SLOT(onQuit()));
    QObject::connect(m_pauseButton, SIGNAL(pressed()), this, SLOT(onPausePressed()));
    QObject::connect(m_pauseButton, SIGNAL(released()), this, SLOT(onPauseReleased()));
    QObject::connect(this, SIGNAL(finished(int)), this, SLOT(onQuit()));
    } // setupUi

The UI designer just lays out the widgets (and possibly statically connects signals to slots).  The code behind the UI is in gui.cpp, which I copied from the example.  After this step, my gui.cpp code is the same as the example, except for Gui parent being QMainWindow instead of QDialog.

State machines

The philosopher and the table state machines drive the application logic.  The Qt integration example has the 2 state machine implementations generated by the QM state charting tool, but I do NOT want to generate my code, so I copy philo.cpp and table.cpp from another example (examples/arm/vanilla/gnu/dpp-at91sam7s-ek) that does not yet use the new style of coding the state transition.  I also added these 2 files to the project.  But I later found out that weird crash can occur if I update the GUI in a non-GUI thread.  Examples of the crash:

QObject::startTimer: Timers cannot be started from another thread
QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
QObject::connect: Cannot queue arguments of type 'QTextBlock'
(Make sure 'QTextBlock' is registered using qRegisterMetaType().)

valgrind  --undef-value-errors=no --leak-check=yes dpp-gui > dpp_valgrind.txt 2>&1

I added the Desktop kit to the project, in the Projects toolbar icon, and reproduced the problem even on Ubuntu.  More errors:



QApplication: Object event filter cannot be in a different thread.
QWidget::repaint: Recursive repaint detected

This is why in the Qt integration example, the table active object it the ONLY active object that derives from GuiQActive class, which is supplied in the port.

class Table : public QP::GuiQActive {
...

Application main

I copied main.cpp verbatim from the example, which gives the table GuiQActive object NO event queue (because events to the GUI go through the Qt event delivery mechanism).  So the following code snippet is correct:

    DPP::AO_Table->start((uint_fast8_t)(N_PHILO + 1),
                         //GuiQActive does not need event queue
                         //&l_tableQueueSto[0], Q_DIM(l_tableQueueSto),
                         (QP::QEvt const **)0, (uint32_t)0,
                         (void *)0, (uint_fast16_t)0);

Build and debug on the target

  1. Leveraging the hard work of setting up the cross-compile in the previous blog entry, I build the target ELF file easily by clicking on the build icon (the hammer).  The debug target is still only 2.3 MB on the disk.
  2. Following the workaround for the cross-debug not working, I copy the ELF file to the target's /root folder.
  3. I start the gdbserver on the copied app, specifying the mouse device (note that this application does NOT use the keyboard, but the keyboard device is event1)

    gdbserver localhost:1234 /root/dpp-gui -plugin evdevmouse:/dev/input/event0
  4. In Qt Creator, attach to the remote gdbserver (menu --> Debug --> Start Debugging --> Attach to Remote Debug Server), specifying the port and the ELF file, as you can see in this example:

I see 5 Homer icons happily taking turns eating, thinking, being hungry!