SAMD10 swd/reset pin - Page 1
Forum section: electronics / general

author: cv007
author_rank: Frequent Contributor
timestamp: March 22, 2021, 03:02:06 pm
content: I have a few small boards made for a SAMD10D14 24pin vqfn and am a little confused about how the reset pin fits into the picture with respect to swd.A simple board with 16 led's, where the swd pins are exclusive for programming, also a pair of pins for possible i2c use and a couple spares for more led's or anything else that may be needed later. A mistake was made where I had an led on the reset pin as I assumed I would just set the pin to gpio. Of course, putting a diode on the reset pin, where you need to get to code to set the pin to gpio, is a bad idea. I was using the SNAP programmer, so also connected the /RST pin to the sam reset pin (with led still attached), and it programmed ok- I assume snap provided enough power to led so voltage drop across led increased (led would light up briefly). My code was setting reset pin to gpio, and was running after programming, but removing power and powering back up the mcu would not start again (even if I held reset pin at 3v). I cut the reset pin trace and moved over to a spare pin and fixed that problem.In the above case, it appears that an mcu held in reset cannot be programmed via swd.Part 2. Setting up the main clock to run at 48MHz, in the Dfll48m().enable() code, I was simply setting the enable bit and leaving all other bits as-is. This would then prevent the mcu from running, and also prevent further programming. So I hook up /RST again and can program. It turns out the ondemand bit is set at reset, and if on it seems since no peripheral is requesting the dfll48m, it is still waiting for 'demand' even when specified for the main clock (I probably have that wrong, or am doing something wrong). Now I just make sure the ondemand bit is cleared unless specifically requested to leave as-is, which works.Code:int main () {Pm().resetPinDisable();Nvmctrl().waitStates( 1 ); // >= 2.7v, 48MHzSysctrl::Osc8m().prescale( Sysctrl::Osc8m::DIV1 ); //8MHzSysctrl::Dfll48m().enable();Sysctrl::Dfll48m().openLoopCoarse(); //use default value=coarse value in otpGclk().generatorSource( Gclk::MAIN, Gclk::DFLL48M );Now the question is- if I was getting 'stuck' with no main clock (assuming it was when switching main clock to dfll48m), and the reset pin was disabled at that point, why can I then program in that state by hooking up the /RST pin to the mcu reset pin?I guess I'm not understanding how the reset pin and swd are interacting with each other. I was also thinking the reset pin was no longer important with swd, but it seems it may be.

author: ataradov
author_rank: Super Contributor
timestamp: March 22, 2021, 04:23:15 pm
content: Can you show a schematic of what is connected to the reset pin and SWD pins? Generally it is not even necessary for programming, but some programmers may get confused if it is not connected.The hard dependency is that SWCLK pin must be high at the time RESET is released. If it is sampled as low, then MCU would be stuck in the reset extension mode.I don't know what this API is, but enabling DFLL required a whole lot of configuration, not just setting of a ENABLE bit. It is possible that openLoopCoarse() does all that. But if you do everything right, connecting the generator to the GCLK0 should request the DFLL.It is hard to tell what actually happens if you just do that. It is possible that you are manipulating the reset pin in a powered off state, and on power on the MCU again gets stuck in the reset extension stare, which lets the debugger access the device.But the access to the reset pin allows the programmer to reset the MCU

author: cv007
author_rank: Frequent Contributor
timestamp: March 22, 2021, 06:37:52 pm
content: >Can you show a schematic of what is connected to the reset pin and SWD pins?It is simply both swd pins going directly to a header, which the SNAP is connected to (and seems to work fine, up until I need the reset pin). The reset pin previously was connected to an led, now is connected to nothing (trace cut). When I need to use the reset pin, I simply touch a jumper from the SNAP /RST connector to a via connected to the reset pin. Normally I do not need to do that, but I seem to be able to get into a position where I need it.>I don't know what this API is, but enabling DFLL required a whole lot of configuration, not just setting of a ENABLE bit.Well, this is my own creation and does get me 48MHz. I'm not quite sure what else I'm supposed to be doing to get 48MHz open loop, but there does not seem to be much involved- turn on dfll48m and set coarse value from otop, which seems to work fine until I leave the ondemand bit set then I require the use of the reset pin to recover. Maybe there is more to it than that, but I'm following the datasheet and that's it. I don't need accuracy, so open loop is used for simplicity. (The previously shown setting of osc8m to div1 is not really needed and was not relevant).Code:struct Dfll48m {//...static inline gclk_registers_t& reg_{ *GCLK_REGS }; //(c++17 inline var)autoenable (bool ondemand = false){u16 v = reg_.SYSCTRL_DFLLCTRL;if( ondemand ) v or_eq ONDEMANDbm; //set ondemand if wantedelse v and_eq compl ONDEMANDbm; //else clearreg_.SYSCTRL_DFLLCTRL = v bitor ENABLEbm; //enable}auto //valid values 0-63openLoopCoarse (u8 v = 64) //default 64 means just use coarse value stored in fuse/otp{if( v == 64 ) {v = *(volatile u8*)(OTP4_ADDR+58/8); //coarse value at bit63:58v >>= (58%8);}if( v > 63 ) v = 63;reg_.SYSCTRL_DFLLVAL = (reg_.SYSCTRL_DFLLVAL bitand compl (0x3F<<10)) bitor (v<<10);}//...class Gclk {//...autogeneratorSource (GENERATOR g, SOURCE s){reg_.GCLK_GENCTRL = GENENbm bitor (s<<8) bitor g;}//...I probably need to read the DSU chapter a few times (or more) to get a handle on how the reset/swd works. I think I incorrectly assumed the reset pin was an exclusive thing on the cpu side, and had no interaction with the swd side of things. On some nRF52 boards I created, I just use the swd pins and the reset pin is never used (or at least has not needed to be used yet). Another mistake I presume, thinking swd is exactly the same on any arm mcu.

author: ataradov
author_rank: Super Contributor
timestamp: March 22, 2021, 06:47:03 pm
content: Even in open loop mode you still need to wait on DFLLRDY flag before using the output. Not sure if this could cause the issue you are seeing.SWD does not need the reset. But SWD needs a system bus to be accessible. If the selected clock is not running or device is sleeping, then the bus would be locked and SWD would fail. In this case having a reset allows the debugger to hold the device in reset with the default clock and perform flash operations.

author: cv007
author_rank: Frequent Contributor
timestamp: March 22, 2021, 08:52:11 pm
content: >Even in open loop mode you still need to wait on DFLLRDY flag before using the output. Not sure if this could cause the issue you are seeing.I added the wait for DFLLRDY before writing to dfll registers as required, but it makes no difference. If the ondemand bit is set, then no clock (or something else) and requires using reset pin with programming to recover. That is the only difference- that 1 bit.>SWD does not need the reset. But SWD needs a system bus to be accessible. If the selected clock is not running or device is sleeping, then the bus would be locked and SWD would fail. In this case having a reset allows the debugger to hold the device in reset with the default clock and perform flash operations.I have the reset pin disabled at the point where it fails, so it seems odd that the reset line can be used to do anything useful at that point, but it does.I now am locked out after trying other things (can no longer program, even with using /RST,reset). I ended up with generator 0 disabled (I knew what I was doing, but figured the reset pin would bring me back in any case). I also had the reset pin disabled in code from before, so now it appears I am really locked out and something else was going on in the previous 'lock out' since in that case the reset pin was still able to get me going via the snap programmer.Time to assemble board #2.My first question should have been to ask if there was a way to end up not being able to program the sam. If there is, I found it, if not, then I should be able to recover.

author: ataradov
author_rank: Super Contributor
timestamp: March 22, 2021, 08:56:49 pm
content: Power off the board, hold the reset low, hold the SWDCLK low, power on the board, release the reset, release SWCLK. This would keep the device in a stuck state, but with the default clock running. So the debugger should be able to recover the device. Keep the reset disconnected from the debugger in case it tries to reset it without SWCLK low.

author: cv007
author_rank: Frequent Contributor
timestamp: March 22, 2021, 09:40:45 pm
content: Thanks. It took a few attempts but I now can program again.

author: cv007
author_rank: Frequent Contributor
timestamp: March 23, 2021, 06:50:21 pm
content: After reading the DSU chapter it makes a little more sense, and explains what I was seeing except for one of the scenarios I described which doesn't really fit.I think I can at least now figure out what state I'm in- whether hot-plugging works or cold-plugging is required. Since the SNAP does not control power (although provides power in my case), then it has to have access to a usable reset pin if it wants to do a cold-plugging. If at a point where the reset pin has been disabled and hot-plugging also no longer works from some reason, then there is no way to get the cold-plugging to work unless the procedure previously described is used (remove power, setup pins, power on, release reset, etc.).I now have a delay in code before doing anything so should have hot-plugging access when powering up at least until delay expires. That is easier in this case than trying to do the cold-plugging where I have to manipulate header pin connections and touch a tiny via in the process (now just pull/reinsert programmer connector, program within x seconds). I don't think this will be needed much, but probably nice to have since the startup delay makes no difference in this case.It would also help if I read the errata already in the datasheet (ok, we have errata listed in the end of datasheets, now how about putting some mark/symbol in the relevant text/description that you may need to check out the errata- maybe they would end up with too many marks and would look too cluttered). Seems like I sometimes forget there are errata.Quote

author: ataradov
author_rank: Super Contributor
timestamp: March 23, 2021, 07:13:04 pm
content: I always add a delay before doing potentially problematic actions like disabling of programming pins or sleeping at least for the debug versions. Even if I know that device is recoverable, I don't want to deal with that during development.Yeah, I forgot about the errata. In my code I always initialize the registers with full value instead of modifying the existing value, so I don't have to worry what is enabled by default.

author: cv007
author_rank: Frequent Contributor
timestamp: March 23, 2021, 09:18:31 pm
content: >In my code I always initialize the registers with full value instead of modifying the existing value, so I don't have to worry what is enabled by default.I generally do the same if possible but sometimes they put too many items in the same register and a decision needs to be made about what functions to create- does enable need to be separate function, or can an enable with 1 or more arguments be a better option, and so on. In my ondemand problem, it would not have helped if I really wanted the ondemand to be set. There are also areas for example where calibration values have been loaded and will want to be preserved, or in the case of the PM example below I would really not want to figure out which mask bits were on by default so just end up with enable/disable and you just get what you asked for and everything else remains unchanged.I usually start with small simple functions and consolidate/modify as usage starts to reveal itself. I like small simple functions since I can easily reason about their input/outcome, but having a bunch of one-time use small functions cluttering up the code is also not desirable.It seems half the battle is coming up with function names and usage that makes sense and is easy to use. C++ seems to help me out quite a bit to get to what I like. I'm not a samD expert, so just code as the need arises, and an example would be the PM peripheral where bus clocks need to be enabled/disabled. It is a little cumbersome to start coming up with something for the four bus mask registers (or just resign yourself to using only the provided headers), but with template usage it can be something more pleasant to use and you get an all-in-one version.The following allows me to do-Pm().busEnable( Pm::TC1, Pm::SERCOM1, Pm::DAC, Pm::RTC );vsPM_REGS->PM_APBCMASK |= PM_APBCMASK_TC_Msk|PM_APBCMASK_SERCOM1_Msk|PM_APBCMASK_DAC_Msk;PM_REGS->PM_APBAMASK |= PM_APBAMASK_RTC_Msk;and I do not need to figure out which peripheral belongs to which bus, nor worry about argument order. The compiler takes the accumulated values after all arguments processed, and if an array mask value is not 0 then writes (or_eq in this case) to the associated register. Very little code produced and in the above case exactly the same. It may seem unnecessary, but once this simple code is in place I now have an easy way to enable/disable a bus. Do this type of thing or similar for more peripherals as needed, and you end up with a nice collection.struct Pm {//...enum AHB_MASK { enum names/values (bitmask) };enum APBA_MASK { enum names/values (bitmask) };enum APBB_MASK { enum names/values (bitmask) };enum APBC_MASK { enum names/values (bitmask) };busMask functions here, also templates (one for each enum, and an empty one to terminate)template<typename...Ts> autobusEnable (Ts...ts){u32 apbx{};busMask(apbx, ts...);if( apbx ) reg_.PM_AHBMASK or_eq apbx;if( apbx ) reg_.PM_APBAMASK or_eq apbx;if( apbx ) reg_.PM_APBBMASK or_eq apbx;if( apbx ) reg_.PM_APBCMASK or_eq apbx;}template<typename...Ts> autobusDisable (Ts...ts){u32 apbx{};busMask(apbx, ts...);if( apbx ) reg_.PM_AHBMASK and_eq compl apbx;if( apbx ) reg_.PM_APBAMASK and_eq compl apbx;if( apbx ) reg_.PM_APBBMASK and_eq compl apbx;if( apbx ) reg_.PM_APBCMASK and_eq compl apbx;}not sure why I produced the large commentary, but its too late now and the damage is already done.

author: ataradov
author_rank: Super Contributor
timestamp: March 23, 2021, 09:28:49 pm
content: For things like flags, I too change individual bits. But I just treat them as part of the peripheral driver. I don't need to have a separate PM controller. SERCOM driver is responsible for enabling its own clock, so there is no confusion. Technically this produces less efficient code, as the same register is updated multiple times, but this is not enough for me to care in a large code base.But for actual peripheral registers, I always write clean values, not expecting anything from the defaults.

author: cv007
author_rank: Frequent Contributor
timestamp: March 23, 2021, 11:20:06 pm
content: >I don't need to have a separate PM controller. SERCOM driver is responsible for enabling its own clock, so there is no confusionThat is also something I would tend to do- let a peripheral take care of itself, giving it enough info to do its job and quit bothering me with the details. If it has options for pins, then tell it what you want and let it deal with setting up its own pins, etc. Some mcu's are easier than others when sorting out pin usage/options.I have more code for avr0/1 and nRF52 than for samD, so the samD has not really been put to much use so do not really know what makes sense yet. The following is an example for an avr0/1, where the 'board' will have the info on what pins to use, what baud rate, whether I want a tx/rx buffer, etc. Different board gets used, a different board gets created/used. Uart and Twi handle their own pins, in this case the lcd driver passes that info along to Twi.inline Boards::ATtiny3217CuriosityNano board; //this is in a common header, everyone gets to use the boardUart uart { board.uartConfig };LcdST7032_Twi lcd { board.twiPins };uart.print( "Hello %s" NEWLINE, "World");lcd.print( "\tHello %s\n", "World" ); // \t is homeThe PM peripheral was simple and there were a few other things in it I wanted like disabling the reset pin (no longer needed), and creating a sleep function. I haven't sorted out all the power saving modes and details, but there are a few peripherals that are bus enabled by default that may want to be disabled outside of the peripheral code (that may not exist), or maybe a peripheral is happy to enable its own bus, but maybe it will make more sense to turn off its bus directly from the Pm class when doing some form of sleep instead of calling back to the peripheral to do it. In any case any other peripheral can now use this class just the same as anyone else so if the Sercom class wants to enable its own bus, then it can also just do-Pm().busEnable( Pm::SERCOM1 ); //don't know which bus we belong to, not our job to know, we just want it on : )orPm().busEnable( (decltype(Pm::SERCOM0))(Pm::SERCOM0<<n) );//if n was provided in instantiation to select which sercom this is, 0,1,2//(if/else brute force probably just as good)