
//------------------------------------------------------------------------------
// Copyright (c) 2012 by Silicon Laboratories. 
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Silicon Laboratories End User 
// License Agreement which accompanies this distribution, and is available at
// http://developer.silabs.com/legal/version/v10/License_Agreement_v10.htm
// Original content and implementation provided by Silicon Laboratories.
//------------------------------------------------------------------------------
// library
#include <si32McuComponent.h>
// hal
#include <si32_device.h>
#include <SI32_PBSTD_A_Type.h>
#include <SI32_PBCFG_A_Type.h>
#include <SI32_CLKCTRL_A_Type.h>
#include <SI32_WDTIMER_A_Type.h>

// TBD myEXTVREG0. this should become #include "myEXTVREG0.h"
#include <SI32_EXTVREG_A_Type.h>

// for profiling with a scope:
#include <SI32_PBSTD_A_Type.h>
uint32 _scope = 0;

// application
#include "myApplication.h"
#include "myCPU.h"
#include "myPB.h"
#include "myI2C0.h"
#include "myTIMER0.h"
#include "myI2S0.h"
#include "myUSB0.h"
#include "myControlPlane.h"
#include "myDataPlane.h"

//------------------------------------------------------------------------------
// This function is invoked by the CMSIS required SysemInit() function in
// system_<device>.c.  SystemInit() is invoked by Reset_Handler() when the
// CPU boots.  Do not assign any variables here since they will be overwritten
// by __main.
void mySystemInit()
{
  // set interrupt priorities.
  NVIC_SetPriority(SysTick_IRQn, (1 << __NVIC_PRIO_BITS) - 1);

  // (enable ITM) Enable APB clock to the Port Bank module.
  SI32_CLKCTRL_A_enable_apb_to_modules_0(SI32_CLKCTRL_0,
                                         SI32_CLKCTRL_A_APBCLKG0_PB0);

  // (enable ITM) Make the SWO pin (PB1.3) push-pull to enable SWV printf.
  SI32_PBSTD_A_set_pins_push_pull_output(SI32_PBSTD_1,
                                         0x00000008);

  // disable the watchdog.
  SI32_WDTIMER_A_stop_counter(SI32_WDTIMER_0);
}

//------------------------------------------------------------------------------
void myApplication_enter_default_mode()
{
  myPB_enter_default_mode();

  // start the usb oscillator
  SI32_USB_A_enable_usb_oscillator(SI32_USB_0);
  #define SI32_USB_A_CLKSEL_TEST_MASK 0x00000080
  SI32_USB_0->CLKSEL.U32 |= SI32_USB_A_CLKSEL_TEST_MASK;
  uint32 tries = 500;
  while((SI32_USB_0->CLKSEL.U32 & SI32_USB_A_CLKSEL_TEST_MASK) && (tries-- > 0));
  si32Assert(tries > 0);
  SI32_USB_A_select_usb_clock_48mhz(SI32_USB_0);
  SI32_CLKCTRL_A_select_ahb_source_usb_oscillator(SI32_CLKCTRL_0);
  SystemCoreClock = 48000000;

  myUSB0_enter_default_mode();
  myI2C0_enter_default_mode();
  myTIMER0_enter_default_mode();
  myI2S0_enter_default_mode();
}

//------------------------------------------------------------------------------
void myApplication_enter_active_mode()
{
  myPB_enter_active_mode();
  myUSB0_enter_active_mode();

///*
  // Experiment successful...  Move this to myEXTVREG0.c
  // Note that this requires an audio card with the vreg wired up.
  // v--EXPERIMENTAL
  SI32_EXTVREG_A_initialize(SI32_EXTVREG_0, 0, 0, 0, 0);  // Initialize all regs
  SI32_EXTVREG_A_select_normal_mode(SI32_EXTVREG_0);
  SI32_EXTVREG_A_select_transistor_type_npn(SI32_EXTVREG_0);
  SI32_EXTVREG_A_set_voltage_output(SI32_EXTVREG_0, 40); // Output 4.0 V
  SI32_EXTVREG_A_enable_weak_pull_up(SI32_EXTVREG_0);
  SI32_EXTVREG_A_enable_module(SI32_EXTVREG_0);
  // ^--EXPERIMENTAL
//*/

  myI2C0_enter_active_mode();
  myTIMER0_enter_active_mode();
  myI2S0_enter_active_mode();
  myControlPlane_initialize();
  myDataPlaneOut_initialize();
  myDataPlaneIn_initialize();
}

//------------------------------------------------------------------------------
void myApplication_initialize()
{
  myCPU_initialize_memory();
  myUSB0_initialize_memory();
  myI2C0_initialize_memory();
  myTIMER0_initialize_memory();
  myI2S0_initialize_memory();
}

//------------------------------------------------------------------------------
void myApplication_print_memory_configuration(void)
{
  // Print the memory configuration for imformational purposes.
  printf("retention ram: 0x%08X - 0x%08x, %dKB\n",
         SI32_MCU_RETENTION_RAM_BASE,
         SI32_MCU_RETENTION_RAM_BASE + SI32_MCU_RETENTION_RAM_SIZE - 1,
         SI32_MCU_RETENTION_RAM_SIZE / 1024);
  printf("standard ram: 0x%08X - 0x%08X, %dKB\n",
         SI32_MCU_RAM_BASE,
         SI32_MCU_RAM_BASE + SI32_MCU_RAM_SIZE - 1,
         SI32_MCU_RAM_SIZE / 1024);
}

//------------------------------------------------------------------------------
void myApplication_print_memory_usage(void)
{
  // Print a memory usage summary to ITM.
  size_t maximum;
  size_t current;
  si32Base_allocation_summary(SI32_HEAP_ZONE, &maximum, &current);
  printf("SI32_HEAP_ZONE cumulative: %d current: %d\n", maximum, current);
  si32Base_allocation_summary(SI32_INCREMENTAL_ZONE, &maximum, &current);
  printf("SI32_INCREMENTAL_ZONE cumulative: %d current: %d\n", maximum, current);
  si32Base_allocation_summary(SI32_RETENTION_ZONE, &maximum, &current);
  printf("SI32_RETENTION_ZONE cumulative: %d current: %d\n", maximum, current);
}

//------------------------------------------------------------------------------
int myApplication_main()
{

  // Configure the clocks.  The system clock needs to match the debugger's ITM clock for printf to work.
  myApplication_enter_default_mode();

  // Booted.
  printf("USB Audio\n");
  si32StartLogging();

  // Configure hardware, allocate memory, as needed.
  // This function initializes several pseudothreads, which depend on having a run-loop
  // in place in order to schedule them. So it must be called after calling
  // si32RunLoopObject_initialize().

  // Remember the current allocation zone.
  si32AllocationZoneEnumType old_zone = si32Base_get_default_allocation_zone();

  // Allocate objects from the incremental zone.
  si32Base_set_default_allocation_zone(SI32_INCREMENTAL_ZONE);

  // Allocate and initialize a run-loop; aka 'cyclic executive' or 'main loop'.
  si32RunLoopObject _loop = si32RunLoopObject();
  si32RunLoopObject* loop = &_loop;
  si32RunLoopObject_initialize(loop);
  si32RunLoopObject_set_capacities(loop,
                                     // number of high priority work requests.
                                     8,
                                     // number of low priority work requests.
                                     16);

  // Configure the number of time intervals that the system timer will provide.
//  si32SystickTimerObject_set_capacity(si32GetSystemTimer(),
//                                        4);

  // Activate the application's hardware.
  myApplication_enter_active_mode();

  // Log memory usage to ITM.
  myApplication_print_memory_configuration();
  myApplication_print_memory_usage();

  // Restore the previous allocation zone.
  si32Base_set_default_allocation_zone(old_zone);

  // In the current implementation the 'main loop' does not return.
  si32RunLoopObject_run(loop);

  // Terminated.
  si32StopLogging();
  printf("Done.\n");

  // Return an exit code.
  return EXIT_SUCCESS;
}

//---eof------------------------------------------------------------------------

