STK500 to UPDI is working! (was: Enabling XTiny UPDI)

Go To Last Post
27 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks to the chips Brian sent me, I have now analyzed the process of how to connect to the UPDI interface. This will eventually allow me to write my STK500v2 to UPDI interface, the objective is to turn a common arduino into a UPDI programmer.

 

But, to start at the beginning, I'm using as sources of information, the xtiny datasheets, of course, but also the pyupdi program done by Atmel people.

 

So, the first thing is to enable the UPDI peripheral. According to the datasheet, this should be done:

 

When the pull-up is detected, the debugger initiates the enable sequence by driving the line low for a minimum of 200ns and a maximum of 1us to ensure that the line is released from the debugger before the UPDI enable sequence is done.

The negative edge is detected by the UPDI, which requests the UPDI clock. The UPDI will continue to drive the line low until the clock is stable and ready for the UPDI to use. The duration of this will vary, depending on the status of the oscillator when the UPDI is enabled. A start-up time between 10us and 200us can be expected.

After this duration, the data line will be released by the UPDI, and pulled-high.

 

But when I looked at the pyupdi code, this initialization sequence was nowhere to be found, instead, a double-break sequence was seemingly used (see datasheet for details on what this is). I could be wrong, because my understanding of python is weak.

 

But anyway, I tested, and indeed the UPDI can be enabled in this alternative way, which greatly simplifies things, because this double-break sequence is also used to recover from errors. The datasheet says:

 

A double BREAK is guaranteed to reset the UPDI from any state.

So it seems, "any state" includes the disabled state, right after power on. And it's so much simpler than the "proper" enable sequence... This should be more clearly explained in the datasheet.

 

Ok, enough talk, I'll show some scope traces now. The yellow trace is the programmer side, blue is target (UPDI) side. Between them is a 4.7k resistor, and VCC is +5V for both.

 

First, a double break sequence:

 

 

This is just as recommended in the datasheet, a pulse of 24.6ms (that is 12 bits at lowest supported baud rate), followed by 1 stop bit, repeated twice.

 

Next trace is a close up on the first falling edge of the first double break sent immediately after powering up the XTiny, you can see that after less than 200ns (this time depends on input capacitance and resistor value), the UPDI reacts by setting the line as low output. Then, after less than 10us, it releases the line, so at the UPDI side, it drifts to a level set by the ratio of the 4.7k resistor and the UPDI pull up. At this moment, the UPDI is enabled.

 

 

 

Finally, on any subsequent double breaks, since the UPDI is already enabled, it doesn't respond by setting a low output state; instead it just stays as input:

 

 

 

Ok, that's it for now, next I will try to start talking to the UPDI.

Last Edited: Fri. Dec 15, 2017 - 07:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, I managed to start talking to the UPDI interface. It was not as straightforward as I expected, because the interface has a curious behavior in output mode that is different from your typical UART, that I will comment about today.

 

As I mentioned, I want my project to run on a mega328p based Arduino. Since the 328P only has one hardware USART and it it is connected to the USB interface of the Arduino, I decided to write a software UART to talk to the UPDI.

 

I tested the software UART with a USB/serial adapter and everything was fine. However, when talking to the UPDI, the results were not very reproducible.

 

So I decided to take a look at the UPDI output with the scope, to see if anything was wrong.

The following trace shows the results. It is important to note that the software UART input was configured with pull-up enabled, I didn't think this would cause any problems. However...

 

 

The blue pulses are for debugging, they mark the moment of sampling.

From the yellow trace, it can be seen that the UPDI only goes output low for a brief moment at the beginning of each bit, then the line is kind of released and starts drifting up because of the pull up on the Arduino side. I say "kind of released", because it doesn't actually drifts to VCC, but to mid rail, as if there was a diode to ground on the UPDI side with somewhat over 2V drop.

 

Now this is not how UARTs usually operate, they keep a strong low for the whole bit, right? With the drift up, the Arduino side doesn't always read the correct input, especially if there is noise.

 

So I had to modify the input routine to disable the pull-up while sampling the data from UPDI. Now, the following trace was obtained:

 

 

Now, this problem is solved and I can talk to the UPDI, send commands and get results. For testing, I read the 16 UPDI internal registers from the Tiny1614 I'm using:

 

00100000 00000000 00000110 00000000 00000000 00000000 00000000 00000000 
00000000 00000011 00000001 10000010 00000000 00000000 00000000 00000000 

The 3rd register, Control A, doesn't have the default value of zero because I changed it in my test program to achieve faster transmission (default is 128 guard bits, I changed to 2 guard bits).

 

Now I think the low level issues are mostly gone, so I can start to write the actual STK500 to UPDI interface.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yes

Looking forward to further UPDates and Information.

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've almost finished implementing the UPDI instruction set, so I was playing writing and reading stuff from the peripherals (not actually programming flash yet).

 

However, I found an undocumented feature while using the reset controller (one of the simplest peripherals of the XTiny). This peripheral has a register that holds flags indicating the causes of the resets, called the Reset Flag Register, RSTFR.

 

While reading this register, everything went as expected, the power on reset flag was set, of course, and after causing a reset via UPDI the corresponding flag is also set. All fine.

 

Then, I wanted to clear these flags. The documentation says that these flags are cleared by writing a "1" to them, so I just wrote 0xFF via the UPDI.

But to my surprise, all the flags where in fact set by this procedure. To clear them, I actually needed to write "0".

 

I also tested with interrupt flags, same thing.

 

My conclusion is that flags that are cleared when the CPU writes "1" to them, do not operate the same way when the UPDI is doing the writing. The UPDI can set and clear these flags at will, like normal bits.

 

I wonder if it's possible to trigger interrupts from the UPDI by writing the corresponding flags. If I remember I'll test this when I get to the stage when I'm actually able to upload a program.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would say this was impossible, because normally the interrupt flags are not writable through any bus at all. It actually sounds like you're using the simulator, where we have allowed exactly this thing.

Which device?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

je_ruud wrote:

I would say this was impossible, because normally the interrupt flags are not writable through any bus at all. It actually sounds like you're using the simulator, where we have allowed exactly this thing.

Which device?

 

It's a real tiny 1614 and not a simulator. Don't tell me this feature is a bug and you want to disable it...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wrote an example program, that dumps the entire contents of the target's address space via the UPDI interface. Then, it will send a 1Hz square wave via the target PB2 pin, that can be used to blink a LED (this wave is sent via the UPDI interface, it's not a program loaded on the target).

 

This program is meant to run on an Arduino class platform with a Mega 328P MCU. The interface with the UPDI target is digital pin 6 of the Arduino (via 4.7k resistor), and with the host system, the interface is the Arduino serial port @115200 baud, this is where the data will be sent, so it can be read/captured with a terminal program.

 

In case there is a problem, the Arduino will start blinking it's LED and instead of the address space, the internal UPDI registers will be dumped for diagnostic.

 

The zip file is an AS7 project and includes precompiled binaries and hex file.

 

If anyone wants to try this with UPDI compatible MCUs, I would be grateful for feedback. The MCU will not be written in any way, only reading will take place.

 

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To get the UPDI response to behave better (and drive the whole bit), you should probably disable the collision detector with the first packet you send, as pyupdi does in the __init__:

 

self.stcs(constants.UPDI_CS_CTRLB, 1 << constants.UPDI_CTRLB_CCDETDIS_BIT)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mraardvark wrote:

To get the UPDI response to behave better (and drive the whole bit), you should probably disable the collision detector with the first packet you send, as pyupdi does in the __init__:

 

self.stcs(constants.UPDI_CS_CTRLB, 1 << constants.UPDI_CTRLB_CCDETDIS_BIT)

 

You mean the behavior I described in post #2? Thanks, I imagined it would be related with collision detection, good to know it can be disabled if needed (I missed that bit in the datasheet :P).

 

Edit: BTW, could you reveal the meaning of bits 1 and 7 of ASI_SYS_STATUS, or is it some kind of secret? I'm asking, because they are set when my Tiny 1614 is operating normally, so they must signal some kind of "normal operation mode".

Last Edited: Sat. Dec 2, 2017 - 10:17 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Finally I finished a semi-working version of the STK500 to UPDI interface.

It can be connected to Atmel Studio as a STK500 programmer, though it lacks most functionality. Basically you can program the target or read back memory contents. But fuses can't be changed and never will in this version, only in the avrdude version when it's ready (one day...). EEPROM can't be changed either, but I will implement it later.

The target can't be fully erased or unlocked, but I use the erase/write command so it is possible to program flash in an unlocked chip without a full erase.

 

Now, studio doesn't support the xtiny series for the STK500, so I had to hack some way to get it working. The 3rd signature byte is replaced by 0x0A so that studio thinks it's another chip (with the same amount of flash).

 

So here is a screenshot:

 

 

As you can see, programing was successful but erase before programing must be unchecked. Notice that Studio thinks this is a Mega164P, but in fact it's a Tiny1614wink

I will attach the hex file so you guys can test this with other xtiny chips, if you want. You will need to burn the binary to an Arduino, then disable auto reset with a capacitor and make the proper connections:

 

 

Using this program, low budget hobbyists can now program the xtiny series with a simple Arduino.

When the source code is a bit more polished, I will post it in the projects section. This is kind of pre-alpha.

Oh, almost forgot, don't change the target voltage parameter from the 5.0V default. Other values enable secret functions of the software.

 

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

the objective is to turn a common arduino into a UPDI programmer.

So it will NOT work with a STK500? You may get sued for misrepresentation in you thread title. wink

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh, I mean it translates the STK500v2 protocol to the UPDI protocol, working as a STK500 emulator or something like that.

Sorry, you thought it was an adapter for the STK500?

I think such a thing would also be possible to do, in theory. Translate ISP commands to UPDI ones. Very messy, though.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Finally I think I have my Arduino based programmer for the xtiny series working.

It can program flash, EEPROM, "user row" and fuses via the normal Atmel Studio interface for the STK500, using a series of hacks that I will explain later. Now I'm too sleepy :P

It can also erase locked chips to unlock them, and dump memory contents (including SRAM and the I/O area).

 

Using this feature, I found that the chip erase command also erases the SRAM to all zeroes. Interesting.

Some bugs are caused because a program assumes the RAM is zero on startup, when in fact is random. However, these bugs may not be detected on a freshly programmed chip on first run, because "chip erase" may have reset all the SRAM.

This can lead to much head scratching...

 

Well, I'll post the code tomorrow. The way to interface it with Studio requires quite a bit of explaining.

Last Edited: Thu. Dec 28, 2017 - 12:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Using this feature, I found that the chip erase command also erases the SRAM to all zeroes. Interesting.

This is normal behaviour for a chip erase issued over any programming interface (ISP, HVPP, HVSP).  GP registers, too.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just never noticed before.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It has spawned many a thread over the years.  "My code seems to work, but only immediately after I program the AVR.  If I reset or power cycle after programming, it stops working!"  This is often a clue that the user's code has one or more uninitialised variables, and that they are assumed to be zero.  This is only guaranteed after a chip erase.

 

I don't believe it is mentioned anywhere in any datasheet, unfortunately.  It makes sense to me though.  Doing a chip erase clears lock bits.  If SRAM and GP registers were left unaffected, an attacker could then load up a simple app or use a debug interface to read out their contents, perhaps revealing keys, passwords, or other details.  The risk is low, to be sure, but I appreciate the effort on the part of the Atmel engineers.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I decided to post the code in GitHub, I guess it's better to place it in a repository:

https://github.com/ElTangas/STK2...

 

More instructions on how to use are in post #10.

 

To unlock a locked tiny817 or similar chip, connect pin D5 of the programmer to gnd and then reset the programmer. This will instruct it to do a full erase of the target.

 

To program the "user row" instead of the EEPROM, go to the "Board Settings" of the STK500 and change Vtarget to something other than 5.5V, and program the EEPROM. The data will go to the "user row". If you change Vtarget to 5.5V, data will go to the EEPROM normally again.

 

If you want to read chunks of the memory space, go to the "Board Settings" and change ARef. If you read the flash, it will instead read from address ARef x 10kB. So the default value of 3.2 volts reads from address 32k, which is the flash memory.

 

If you program the flash, the programmer will always write to the flash address of 32kB irrespective of the value of ARef, but if verify is selected and ARef is not 3.2V, the verify will fail because data will be read from the wrong address.

 

To program the fuses, go to the "Lock bits" tab and program the LOCKBIT register with the fuse address. For example, to lock the device, program LOCKBIT with 0x0A.

Then, go to the "Fuses" tab; the "EXTENDED" and "HIGH" fuses will display the current fuse address, while the "LOW" fuse will display the current fuse value.

To change the fuse, set "LOW" to the new value then program the fuses. There are a total of 32 fuse addresses, but only a few are used, the others should be considered reserved, I wouldn't touch them (I have, of course... the ones I changed don't seem to do anything, actually).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a look at #2

it looks like a UART in IrDA mode!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My program had a nasty bug affecting the upload of data to the flash, caused by a misunderstanding on the operation of some UPDI instructions.

The UPDI module has direct and indirect addressing instructions; indirect addressing uses an internal pointer register.
I believed direct addressing would not change this pointer, but it does, therefore it needs to be saved before using a direct addressing instruction if the pointer value is needed later.

Basically, this caused a mess in the upload function, but now it's solved (I hope).

 


Note to Micromel people: this behavior is not explicit in the datasheet, it should be more clear in the UPDI instruction descriptions.


 

Now I'm working on the avrdude version of my UPDI interface, it's going well, but I have to deal with some limitations. Avrdude has a few hardcoded memory types that don't map exactly to the xtiny ones, but I'll devise some workaround.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you aware that we upstreamed UPDI support in avrdude (using the j3 protocol). I guess it was finalized not that many days ago so the code is pretty fresh... But might be interesting to peek at..

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, I see. It's the contributions by Jan Egil Ruud, right? I'll take a look.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jupp. Think Jörg merged them just a couple of days back...

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I took a look the avrdude patches, it seems a good job in getting avrdude to talk to modern Atmel tools to program UPDI chips, that is understandably your goal.

My goal is just different, I want avrdude to talk to UPDI chips using cheap Arduino clones, so I don't want to patch avrdude or use a modern protocol.

I'm using the stk500v2 protocol. This means I will have to use chip definitions for the avr8x, as you call them, that are different from the official ones you added to avrdude.conf, so I will use some kind of suffix to tell the difference, like "t1614_isp".

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A minor cog in the inner workings of avrdude is interfering with my plans! So I decided to report it as a bug, even though it doesn't manifest itself unless you get creative with the avrdude.conf file, like I'm doing...

Probably it will just be ignored by the developers, but maybe not since it's very simple to fix. The problem is if it breaks something, hopefully not.

 

So, what did I want to do? Since UPDI chips have so many fuses, why not access them all as a block? I found that the xtiny has a total of 32 fuse bytes, though most are undefined, and they form a contiguous block.

I thought: "Easy peasy, I'll define the "fuse" memory in avrdude.conf to be 32 bytes instead of 1 byte. Damn, I'm smart!", and added this line to my t1614 definition in avrdude.conf:

 

     memory "lfuse"                      /* All the Fuses */
        size            = 32;
        read            /* One of the bytes must contain an "o" to mark pollindex byte */
                        =       "0  0  0  0   0  0  0  0   o  0  0  1   0  0  1  0",
                                "1  0  0  a4  a3 a2 a1 a0  0  0  0  0   0  0  0  0";
        ;

I'll skip the details, but bytes 1 and 2 ( o 0 0 1 0 0 1 01 0 0 a4 a3 a2 a1 a0) form the fuse address, 0x1280 + |a4 a3 a2 a1 a0|. So what do I get from my experimental code?

 

C:\Users\JMR_2\Desktop\temp>avrdude -c stk500v2 -b 500000 -P com5 -p t1614 -t

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.03s

avrdude: Device signature = 0x1e9422 (probably t1614)
avrdude> dump lfuse
>>> dump lfuse
0000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
0010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

avrdude>

 

All zeroes? But it was working fine with other memory types, for example:

     memory "lock"                      /* All the Fuses */
        size            = 32;
        read            /* One of the bytes must contain an "o" to mark pollindex byte */
                        =       "0  0  0  0   0  0  0  0   o  0  0  1   0  0  1  0",
                                "1  0  0  a4  a3 a2 a1 a0  0  0  0  0   0  0  0  0";
        ;
C:\Users\JMR_2\Desktop\temp>avrdude -c stk500v2 -b 500000 -P com5 -p t1614 -t

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.03s

avrdude: Device signature = 0x1e9422 (probably t1614)
avrdude> dump lock
>>> dump lock
0000  00 00 02 ff 00 f6 06 00  00 ff c5 ff ff ff f1 02  |................|
0010  40 2a 45 e4 86 44 a4 54  d1 d3 85 46 94 54 0f 00  |@*E..D.T...F.T..|

avrdude>

That's how it's supposed to look! So finally I figured out that avrdude just ignores address bits a4 a3 a2 a1 a0 in the conf file for fuse memories, and replaces it by hardcoded addresses 0, 1 and 2 for lfuse, hfuse and efuse, so the same byte is read repeatedly.

 

So for now (forever?), I will use the "lock" memory to read/write all the fuses. That's bad because I wanted to access other UPDI memories using the different fuses (peripherals, RAM, etc.), it would probably allow using avrdude in interactive mode to do some limited debugging...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas. Thank you for sharing your work for others to use. I have a question. I have a few Attiny814 that I purchased and I want to make a prototype before I start to design a PCB with Attiny814 as the brain. I simply want to read a temperature from a thermocouple and store the values in an SD card using Attiny814. While reading the datasheet of Attiny814, it says it uses UPDI to program the chip. Can I use your tool to program the Attiny814 to do this?

 

Sorry if I sound extremely stupid. I am a mechanical engineer with little experience in MATLAB, LabView and Arduino programming, venturing into Atmel Studio and PCB building. Your reply will be mighty helpful. 

 

--

Vish 

---
Vish
An automotive engineer learning about uCs

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sure, you can use this tool: https://github.com/ElTangas/jtag...

Other things you need:

  • Write your program, for example with Atmel Studio or Arduino IDE
  • Learn how to use the "avrdude" program
  • An arduino to load my tool and use as programmer

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks a lot man! You are a lifesaver! 

---
Vish
An automotive engineer learning about uCs