K20 Suggestion: Add peripheral base addresses to linker script

christoph

Well-known member
I'm writing structs and classes for the K20 peripherals, so I needed their base addresses in the linker script:
Code:
/* hardware struct symbols, sorted by peripheral bridge slots (chapter 4.5.1) */
_DMA      = 0x40008000; /* chapter 21 */
_DMA_TCDn = 0x40009000; /* chapter 21 */
_FMC      = 0x4001F000; /* chapter 27 */
_FTFL     = 0x40020000; /* chapter 28 */
_DMAMUX0  = 0x40021000; /* chapter 20 */

_SPI0     = 0x4002C000; /* chapter 42 */
_I2S0     = 0x4002F000; /* chapter 45 */
_CRC      = 0x40032000; /* chapter 30 */
_USBDCD   = 0x40035000; /* chapter 40 */
_PDB0     = 0x40036000; /* chapter 33 */

_PIT      = 0x40037000; /* chapter 35 */
_FTM0     = 0x40038000; /* chapter 34 */
_FTM1     = 0x40039000; /* chapter 34 */
_ADC0     = 0x4003B000; /* chapter 31 */
_RTC      = 0x4003D000; /* chapter 38 */

_VBAT     = 0x4003E000; /* chapter ?? */
_LPTMR0   = 0x40040000; /* chapter 36 */
_SYSREG   = 0x40041000; /* chapter ?? (system register file) */
_TSI0     = 0x40045000; /* chapter 47 */
_SIM      = 0x40047000; /* chapter 12 */

_PORTA    = 0x40049000; /* chapter 11 */
_PORTB    = 0x4004A000; /* chapter 11 */
_PORTC    = 0x4004B000; /* chapter 11 */
_PORTD    = 0x4004C000; /* chapter 11 */
_PORTE    = 0x4004D000; /* chapter 11 */

_WDOG     = 0x40052000; /* chapter 23 */
_EWM      = 0x40061000; /* chapter 22 */
_CMT      = 0x40062000; /* chapter 37 */
_MCG      = 0x40064000; /* chapter 24 */
_OSC      = 0x40065000; /* chapter 25 */

_I2C0     = 0x40066000; /* chapter 43 */
_UART0    = 0x4006A000; /* chapter 44 */
_UART1    = 0x4006B000; /* chapter 44 */
_UART2    = 0x4006C000; /* chapter 44 */
_USB0     = 0x40072000; /* chapter 39 */

_CMP0     = 0x40073000; /* chapter 32 */
_LLWU     = 0x4007C000; /* chapter 16 */
_PMC      = 0x4007D000; /* chapter 15 */
_SMC      = 0x4007E000; /* chapter 14 */
_RCM      = 0x4007F000; /* chapter 13 */

_GPIOA    = 0x400FF000; /* chapter 46 */
_GPIOB    = 0x400FF040; /* chapter 46 */
_GPIOC    = 0x400FF080; /* chapter 46 */
_GPIOD    = 0x400FF0C0; /* chapter 46 */
_GPIOE    = 0x400FF100; /* chapter 46 */
With this, I can declare
Code:
class DSPI_REGS
{
  public:
    volatile uint32_t MCR;   // +0x00

  private:
    uint32_t _padding0;      // +0x04

  public:
    volatile uint32_t TCR;   // +0x08
    volatile uint32_t CTAR0; // +0x0C
    volatile uint32_t CTAR1; // +0x10

  private:
    uint32_t _padding1[6];

  public:
    volatile uint32_t SR;    // +0x2C
    volatile uint32_t RSER;  // +0x30
    volatile uint32_t PUSHR; // +0x34
    volatile uint32_t POPR;  // +0x38
    volatile uint32_t TXFR0; // +0x3C
    volatile uint32_t TXFR1; // +0x40
    volatile uint32_t TXFR2; // +0x44
    volatile uint32_t TXFR3; // +0x48

  private:
    uint32_t _padding2[12];

  public:
    volatile uint32_t RXFR0; // +0x7C
    volatile uint32_t RXFR1; // +0x80
    volatile uint32_t RXFR2; // +0x84
    volatile uint32_t RXFR3; // +0x88
} __attribute__((packed,aligned(4)));

template<DSPI_REGS& base>
class DSPI : public DSPI_REGS
{
  class CTARn
  {
    public:
      volatile uint32_t& operator[](const uint8_t& i) const
      {
        volatile uint32_t* p = &(base.CTAR0);
        return p[i & 1];
      }
  };
  class TXFRn
  {
    public:
      volatile uint32_t& operator[](const uint8_t& i) const
      {
        volatile uint32_t* p = &(base.TXFR0);
        return p[i & 3];
      }
  };
  class RXFRn
  {
    public:
      volatile uint32_t& operator[](const uint8_t& i) const
      {
        volatile uint32_t* p = &(base.RXFR0);
        return p[i & 3];
      }
  };
  public:
    CTARn CTAR;
    TXFRn TXFR;
    RXFRn RXFR;
}

extern DSPI_REGS _SPI0;
DSPI<_SPI0>& SPI0 = (DSPI<_SPI0>&)_SPI0;
And use it as in
Code:
SPI0.CTAR[0] = SPI_CTAR_FMSZ(7) ...;
Which CTAR to use can now be specified in templates classes that build on top of the SPI (that's what I actually needed when this journey started).

I've done the same thing for the DMA and came up with an SPI driver that uses DMA for Tx (sort of) and Rx. AND it seems to work. I'll provide it if you're interested.

Regards

Christoph
 
In the latest 1.17-rc2, I created an alternate definition for the SPI port using a struct and base address.

Could you please take a look at that and see if it meets your needs?

I'm a bit reluctant to define all this chip specific stuff twice, duplicating it all in the linker file. But if there's some really compelling advantage to doing so, well, I guess that advantage would need to be worth the extra long-term maintenance.
 
Indeed, defining things twice is not very elegant, and it's prone to errors.

I'll take a look at your SPI definition and see if it works for me.
 
Hi Paul,

currently SPI0 is defined in mk20dx128.h. I had a look at your SPI struct and it doesn't give me the flexibility I need. However, because the header defines it, this interferes with my own definition of SPI0, so I had to comment it out. On the other hand, making the base address known in the linker script would give us both the possibility to define SPI0 however we like. This would also take complexity out of the chip header file, which in my opinion should not exactly define how things can be used. This should be the job of specialized headers. On yet another hand, I'm aware of the fact that this is against the arduino philosophy. Indeed I really like the simpler functions like pinMode().

Regards

Christoph
 
Hi Paul,

is "no" your final answer? If so, I'll modify my DMA SPI code so that it doesn't rely on those symbols and can be used with the unmodified Teensy linker script.

Regards

Christoph
 
Back
Top