ATSAMD09/D21 - any writes to the GCLK resource appears to crash the part? - Page 1
Forum section: electronics / general

author: XFDDesign
author_rank: Frequent Contributor
timestamp: August 27, 2023, 04:59:57 pm
content: While my bread-and-butter micro has been the SAMD51, I have an application where the ATSAMD09 is the perfect fit (by spec). I don't actually need much more than to configure some clocks, as all I'm using the D09 for is as a integer-N prescalar. As such, the core of my need is to manipulate the GCLK resources. Reading over the DS, unlike the D51 where you can manipulate the resource through individual bit operations, the D09/D21 must be written to "in bulk" or with all settings in one go. No problem, I can switch my methods to just OR'ing values. The datasheet also indicates you must 1. set the divisor first, 2. set the generator resource, then 3. set the Clock resource (if you're using it).My finding is that any write to the GCLK peripheral crashes the part. For example:pGCLK->generatorDivisor = DIVIDE_BY_1 | GCLK_ID1;Locks the part. Using the SAMICE debugger, you can pause before hand and it responds fine. You step into this instruction, and the system hangs. Pausing the debugger does nothing.Now traditionally, this kind of behavior has me looking at the address mapping of a peripheral, and then to the register itself should the general mapping be correct. In this case, the root resource GCLK is correctly mapped to 0x4000C00; the division register is correctly offset by 8 bytes (0x4000C08). Other issues I have considered was perhaps (somehow) the GCLK resource in the power manager was janky, but no, the GCLK resource is enabled (1) in APBAMASK.Now proceeding, just to be thorough, I went to the generator control. The following line:pGCLK->generatorControl = GCLK_ID1 | GCLK_GEN_SOURCE_OSC8M | GCLK_GEN_ENABLE | GCLK_OUTPUT_ENABLE; // Use the int 8M osc, route to gen 1, enable output and generatorAlso causes the part to lock up. And both do this on ID0 as well. The current program, just to figure out the problem I'm having with GCLK, is:Code:int main(void){volatile uint32_tconfigWord=0;// Setup the portpPortA->directionSet = (1 << 9);// pa09 to outputpPortA->pinMux.member.word = 0x66;pPortA->pCfg.member.bits.pmuxEnable=1;pPortA->pCfg.member.bits.inputEnable=1;pPortA->pCfg.member.bits.pmuxEnable=1;pPortA->pCfg.member.bits.driveStrength=1;pPortA->directionSet = (1 << 14);pPortA->out = 0;pSysControl->XOSC.word=0x2;pSysControl->OSC8M.bits.prescalar = 0;pGCLK->control.bits.reset = 1;// reset the GCLK resourcepGCLK->generatorDivisor = DIVIDE_BY_1 | GCLK_ID1;// setup a baseline division rate for GCLK1pGCLK->generatorControl = GCLK_ID1 | GCLK_GEN_SOURCE_OSC8M | GCLK_GEN_ENABLE | GCLK_OUTPUT_ENABLE; // Use the int 8M osc, route to gen 1, enable output and generatorwhile (1){pPortA->outToggle = (1<<14);}}The aim is to just toggle a pin if I can get through the clocking resource, but thus far it's all a miss. Does anyone have any inputs or ideas on what I'm missing?

author: cv007
author_rank: Frequent Contributor
timestamp: August 28, 2023, 03:13:21 am
content: QuoteI would assume these are typos, with a missing 0, but if not that could be a problem.Your uppercase defines/enums/const's could be suspects as we cannot see if these are shifted values or not (they do not look like manufacturer defines). So, for instance you may be setting the id to some reserved value with unknown results if these are not shifted values.This example is for a samD10, which is similar enough to the D09. It blinks an led from GCLK1 at 1Hz. The startup code is default generated, which is not doing anything special. The mcu headers were used for the registers only, the enums were produced from looking at the datasheet as its faster than finding/matching define names from headers to datasheet.Code:#include <sam.h>#include <stdint.h>#include <stdbool.h>//functions are example specific, not complete//no clock synchronization checks done//------------------------------------------------------------------------------//SYSCTRL//------------------------------------------------------------------------------typedef enum {SYSCTRL_OSC8M_DIV1, SYSCTRL_OSC8M_DIV2, SYSCTRL_OSC8M_DIV4, SYSCTRL_OSC8M_DIV8 }sysctrl_osc8m_div_t;static inline voidsysctrl_osc8m_div (sysctrl_osc8m_div_t e){uint32_t bmclr = ~(3<<8); //PRESC[9:8]uint32_t bmset = e<<8;volatile uint32_t* preg = &SYSCTRL_REGS->SYSCTRL_OSC8M;*preg = (*preg & bmclr) | bmset;}//------------------------------------------------------------------------------//PORT//------------------------------------------------------------------------------typedef enum {PORT_ALT_A, PORT_ALT_B, PORT_ALT_C, PORT_ALT_D, PORT_ALT_E,PORT_ALT_F, PORT_ALT_G, PORT_ALT_H }port_altfunc_t;static inline voidport_altfunc (const uint8_t pin, port_altfunc_t af){ //mcu w/PORTA onlyif( pin > 31 ) return; //invalid pin, do nothinguint8_t bmclr = pin&1 ? ~0xF0 : ~0x0F;uint8_t bmset = pin&1 ? af<<4 : af;volatile uint8_t* preg = &PORT_REGS->GROUP.PORT_PMUX[pin/2];*preg = (*preg & bmclr) | bmset;PORT_REGS->GROUP.PORT_PINCFG |= 1; //PMUXEN}//------------------------------------------------------------------------------//GCLK//------------------------------------------------------------------------------typedef enum {GCLK0, GCLK1, GCLK2, GCLK3, GCLK4, GCLK5 }gclk_id_t;typedef enum {GCLK_OSCULP32K = 3, GCLK_OSC8M = 6 }gclk_src_t;static inline voidgclk_generator (gclk_id_t id, gclk_src_t src, const uint16_t div, const bool output){GCLK_REGS->GCLK_GENDIV = div<<8 | id;GCLK_REGS->GCLK_GENCTRL = output<<19 | /*ENABLE*/1<<16 | src<<8 | id;}//------------------------------------------------------------------------------//MAIN//------------------------------------------------------------------------------intmain (){//sysctrl_osc8m_div( SYSCTRL_OSC8M_DIV1 ); //works, not needed hereport_altfunc( 9, PORT_ALT_H ); //GCLKIO->PA09 (led connected)gclk_generator( GCLK1, GCLK_OSCULP32K, 32768, true ); //output enable//PA09 blinks at 1Hz (osculp32k, div=32768)while(1){}}

author: westfw
author_rank: Super Contributor
timestamp: August 28, 2023, 06:13:36 am
content: Quote from: XFDDesign onTodayat 09:59:57 amIs there some reason you're not using the Microchip-provided symbol definitions:Code:GCLK->GENDIV.reg = ((2 << GCLK_GENDIV_DIV_Pos) | (0 << GCLK_GENDIV_ID_Pos));

author: XFDDesign
author_rank: Frequent Contributor
timestamp: August 28, 2023, 12:40:44 pm
content: Quote from: cv007 on August 28, 2023, 03:13:21 amYes, typo there with the comment. Here is the list of address defines I use:Code:#define PAC0_ADDRESS0x40000000#define PM_ADDRESS0x40000400#define SYSCTRL_ADDRESS0x40000800#define GCLK_ADDRESS0x40000c00#define WDT_ADDRESS0x40001000#define RTC_ADDRESS0x40001400#define EIC_ADDRESS0x40001c00#define PAC1_ADDRESS0x41000000#define DSU_ADDRESS0x41002000#define NVMCTRL_ADDRESS0x41004000#define PORT_ADDRESS0x41004400#define DMAC_ADDRESS0x41004800#define MTB_ADDRESS0x41006000#define PAC2_ADDRESS0x42000000#define EVSYS_ADDRESS0x42000400#define SERCOM0_ADDRESS0x42000800#define SERCOM1_ADDRESS0x42000C00#define TC1_ADDRESS0x42001800#define TC2_ADDRESS0x42001C00#define ADC_ADDRESS0x42002000Directly copy/pasting the example program used fails to compile.Quote from: westfw on August 28, 2023, 06:13:36 amI've not had a positive experience with ASF, finding it poorly documented, equal parts verbose and obfuscating and generally not a value add. Early efforts in using Atmel's framework didn't do well to communicate configuring hardware, and looking through the code found functions that refer to other functions which referred to skeletal functions, which... etc.For what it is worth, for all of my other developments (SAMD51, SAME70/SAMS70) the abandoning of atmel's clunky framework has been without issue.

author: XFDDesign
author_rank: Frequent Contributor
timestamp: August 28, 2023, 12:54:12 pm
content: One incidental:You can readback the current setting of a given GCLK GeneratorControl resource through writing ID to the register and then reading back the GCLK GenControl register. Reading both GCLK0 and GCLK1 do correctly report the Power-On-Default values per the DS.

author: cv007
author_rank: Frequent Contributor
timestamp: August 28, 2023, 04:10:34 pm
content: QuoteNot a surprise as its using the manufacturer headers for the register access, and there are several flavors it seems. You would have to translate the register access lines to whatever you have in use. Figured you could handle that if/when you saw the error messages pertaining to the registers.Complete, no manufacturer headers. Still blinks the led from GCLK1-Code:#include <stdint.h>#include <stdbool.h>//functions are example specific, not complete//no clock synchronization checks done//------------------------------------------------------------------------------//SYSCTRL//------------------------------------------------------------------------------enum { SYSCTRL_BASE = 0x40000800 };struct sysctrl_s { uint8_t unused_; uint32_t OSC8M; };volatile struct sysctrl_s* const SYSCTRL = (struct sysctrl_s*)SYSCTRL_BASE;typedef enum {SYSCTRL_OSC8M_DIV1, SYSCTRL_OSC8M_DIV2, SYSCTRL_OSC8M_DIV4, SYSCTRL_OSC8M_DIV8 }sysctrl_osc8m_div_t;static inline voidsysctrl_osc8m_div (sysctrl_osc8m_div_t e){uint32_t bmclr = ~(3<<8); //PRESC[9:8]uint32_t bmset = e<<8;SYSCTRL->OSC8M = (SYSCTRL->OSC8M & bmclr) | bmset;}//------------------------------------------------------------------------------//PORT//------------------------------------------------------------------------------enum { PORT_BASE = 0x41004400 };struct port_s { uint8_t unused_; uint8_t PMUX; uint8_t PINCFG; };volatile struct port_s* const PORT = (struct port_s*)PORT_BASE;typedef enum {PORT_ALT_A, PORT_ALT_B, PORT_ALT_C, PORT_ALT_D, PORT_ALT_E,PORT_ALT_F, PORT_ALT_G, PORT_ALT_H }port_altfunc_t;static inline voidport_altfunc (const uint8_t pin, port_altfunc_t af){ //mcu w/PORTA onlyif( pin > 31 ) return; //invalid pin, do nothinguint8_t bmclr = pin&1 ? ~0xF0 : ~0x0F;uint8_t bmset = pin&1 ? af<<4 : af;PORT->PMUX[pin/2] = (PORT->PMUX[pin/2] & bmclr) | bmset;PORT->PINCFG |= 1; //PMUXEN}//------------------------------------------------------------------------------//GCLK//------------------------------------------------------------------------------enum { GCLK_BASE = 0x40000C00 };struct gclk_s { uint8_t unused_; uint32_t GENCTRL; uint32_t GENDIV; };volatile struct gclk_s* const GCLK = (struct gclk_s*)GCLK_BASE;typedef enum {GCLK0, GCLK1, GCLK2, GCLK3, GCLK4, GCLK5 }gclk_id_t;typedef enum {GCLK_OSCULP32K = 3, GCLK_OSC8M = 6 }gclk_src_t;static inline voidgclk_generator (gclk_id_t id, gclk_src_t src, const uint16_t div, const bool output){GCLK->GENDIV = div<<8 | id;GCLK->GENCTRL = output<<19 | /*ENABLE*/1<<16 | src<<8 | id;}//------------------------------------------------------------------------------//MAIN//------------------------------------------------------------------------------intmain (){//sysctrl_osc8m_div( SYSCTRL_OSC8M_DIV1 ); //works, not needed hereport_altfunc( 9, PORT_ALT_H ); //GCLKIO->PA09 (led connected)gclk_generator( GCLK1, GCLK_OSCULP32K, 32768, true ); //output enable//PA09 blinks at 1Hz (osculp32k, div=32768)while(1){}}https://godbolt.org/z/ejeq7jjcnand since functions were created, can easily use more gclkio w/led's-https://godbolt.org/z/EnGa96rM7

author: XFDDesign
author_rank: Frequent Contributor
timestamp: August 28, 2023, 04:36:12 pm
content: Credit where it's due, this works. Thanks! I'll work to figure out what is functionally different. My best guess is that I'm missing some kind of configuration step.

author: westfw
author_rank: Super Contributor
timestamp: August 28, 2023, 06:07:59 pm
content: QuoteUsing the provided CMSIS headers is a lot less obnoxious and intrusive than using ASF (the set of library functions)!I don't like ASF either, but it would take much more extreme problems for me to reject the hardware definition .h files provided by a vendor. (the directory structure of the rp2040almostdoes it.)For instance, My immediate guess as to your problem is that you have the structure for GLCK defined wrong (it's MUCH different between SAMD9 and SAMD51.) But I really don't want to wade through your definitions and compare them to the official versions.