micronucleus / micronucleus

ATTiny usb bootloader with a strong emphasis on bootloader compactness.
Other
1.67k stars 382 forks source link

micronucleus for ATmega328P #107

Closed AHorneffer closed 5 years ago

AHorneffer commented 6 years ago

[I started a new topic, because I believe this will be a longer thread and I wanted it to be easily found.] I got the version in my atmega328p branch to partially work on an ATmega328P with an external 16MHz crystal.

What does work is that it registers as an USB device, talks to the micronucleus command-line program, and writes something into the flash memory of the atmega.

As far as I can tell there are these problems at the moment:

I would also appreciate any suggestions how to debug this.

AHorneffer commented 6 years ago

O.K. I used avrdude to read out the flash-memory of the m328p and got the following:

The second item on my list (only the first load of an application worked - somewhat) was because the erase didn't work. But adding an boot_spm_busy_wait(); after the erase for each page seems to have solved this issue.

The third one is because one word (2 bytes) in each page doesn't get written correctly. Which word it is is affected by the address given in the boot_page_write() call:

One really strange thing is that the jump at address 0x0 and the jump at the last address before the bootloader don't seem to be affected.

(P.S. that my simple "blink" application works is apparently blind luck.)

AHorneffer commented 6 years ago

I managed to track down the two issues:

The corrupted data was caused by the code to clear page buffer when a transfer_page commands was received. On the m328p this was effectively a page loading operation, and the line from the data-sheet "It is not possible to write more than one time to each address without erasing the temporary buffer." translates to: "If an address is written to twice, then only bits that were set in both writes are set in the final value." My solution seems to work for a m328p. But while I tried to make it safe I cannot test it for the other systems.

The other issue was that apparently the idle timer was reset on any USB communication, not only on communication with the command-line tool. So it got reset every time the OS was bugging the device with something. Btw. Am I the only one who had this problem? This doesn't seem to be a m328p specific issue.

mihaigalos commented 6 years ago

I understand the boot_spm_busy_wait() call, but I think this should take place in micronucleus_lib.c::micronucleus_eraseFlash(), and substitute the following line:

delay(((float) deviceHandle->erase_sleep) / 100.0f);

Did you push your findings to a new branch? we can use a #ifdef __AVR_ATmega328P__ to isolate the m328p code from the others.

mihaigalos commented 6 years ago

The other issue was that apparently the idle timer was reset on any USB communication, not only on communication with the command-line tool. So it got reset every time the OS was bugging the device with something.

Perhaps hooking the board up to 3V3 or 5V directly and see if this still happens. I think it should always happen if there is no application code and the bootloader is called immediately after a previous bootloader run and a reset.

AHorneffer commented 6 years ago

Did you push your findings to a new branch?

Yes. It is at: https://github.com/AHorneffer/micronucleus/tree/atmega328p (It's what the atmega328p link in the first post points to.) But I merged quite a few of the open pull requests before adding my changes, so keep that in mind when checking the differences. (And it is the reason why I haven't started a pull request.)

The boot_spm_busy_wait() needs to go into the firmware, not the client!

Perhaps hooking the board up to 3V3 or 5V directly and see if this still happens.

Well, yes, that worked. Have a look at my last commit to my atmega328p branch. That fixed the issue.

mihaigalos commented 6 years ago

Awesome. Confirmed working with your changes, (flashed a blink sketch). If you only flash the bootloader (no upgrade.hex), I think the start address can be 7A00 instead of 7800. That would give you 512 extra bytes of opcode.

mihaigalos commented 6 years ago

Update: I can only flash using the bootloader only once. I can confirm the bootloader is still in place @7800h by dumping the flash to a hex. After the first flash, I did reset (multiple times). Micronucleus did not run (no blinking led). Instead, the application code started directly..

Fuses : Lo : 0xEF, Hi : 0xD8, Ext: 0xFF

Anybody else experience this?

AHorneffer commented 6 years ago

The High-Fuse 0xD8 has the bootloader flash size set to 2048 words (aka 4096 Bytes) and the reset vector set to the start of the bootloader flash. So after a reset the MCU will start processing commands at address 0x7000. Before you flashed anything, that area is empty until it encounters the bootloader code. After flashing the first application, the last instruction before the bootloader code is a jump to the start of the application. (Thats how the bootloader remembers where it can start the application.)

To get it running set the fuse to the value I gave in the makefile (0xda).

This also answers the question in your previous post: The bootloader flash size can only be set to 512, 1024, 2048, or 4096 bytes.

AHorneffer commented 6 years ago

This also answers the question in your previous post: The bootloader flash size can only be set to 512, 1024, 2048, or 4096 bytes.

Hmmm... Maybe it also works with the BOOTRST not enabled. (I.e. with the MCU starting at the beginning of the flash after a reset.) In that case the bootloader flash size set in the fuses should be smaller than the actual bootloader size. (So that the bootloader doesn't try to write into the bootloader flash area.) That would mean:

Feel free to give it a try. Keep in mind that a real test would require to upload an application that is larger than 30 kByte.

mihaigalos commented 6 years ago

Thank you. I now understand how it all comes together.

AHorneffer commented 6 years ago

With 0xD8 you set the size of the boot region to 4096 Byte, the boot region is always at the end of the flash, so it will start at address 0x7000. The BOOTRST bit in the high-fuse changes the rest-vector from 0x0000 to the start of the boot region. Together that means a high-fuse setting of 0xD8 will make the MCU start at address 0x7000 after a reset.

The solution is to either have the bootloader start at the address given by the boot region - which is the version I used - or to not activate the BOOTRST bit - which might then allow a bootloader size (and start address) that is not one of the values given by the BOOTSZ bits.

mihaigalos commented 6 years ago

+1 for an extra precise answer.

AHorneffer commented 6 years ago

O.K. If the BOOTRST is not enabled, then it is indeed possible to that extra 512 bytes for applications. (See the commit to my repository.) But in contrast to what I thought the boot region as set by the fuses must be larger than the bootloader because while the bootloader can write to the boot region flash area, but code that is not in that area cannot write to the flash at all. (The spm instruction is disabled when executed outside the boot region.)

I tested it with an application that has a large data section in the program space. As the actual code of that application is at the end (and thus the boot region) I'm confident that it works.

This now could also enable the "upgrade" application, one would "just" have to make sure that the write-to-flash code of that application is in the boot region.

mihaigalos commented 6 years ago

I thought the boot region as set by the fuses must be larger than the bootloader because while the bootloader can write to the boot region flash area, but code that is not in that area cannot write to the flash at all.

The lock bits can disable LPM, SPM or both in the boot region, if necessary. If they are unset, the bootloader can write anywhere.

I tested it with an application that has a large data section in the program space.

If you are talking about static const variables that only get copied over from flash to ram, consider making them flash constants : suffix them with a PROGMEM attribute and using pgmread.* to read them directly from the flash. See this for further reference.

tykefcz commented 6 years ago

Hello i have patched and tested version of micronucleus for atmega8, atmega328 without bootloader page settings by fuses. You can test it at my fork. I ported MN to atmega8/328 cca a year ago. But til now I not make a time to finalize patches. Then now I have remastered build process:

NuclearPhoenixx commented 6 years ago

Sorry if this is kind of off-topic, but what advantage does micronucleus bring over e.g. optiboot for the ATmega328P, except for a smaller bootloader size?

tykefcz commented 6 years ago

Advantages for me - smaller bootloader size, one commandline utility for both digispark and my boards, easy configurable bootloader entry method, no fuses reprogram if bootloader size is changed. And if flash size is small by some bytes using micronucleus as library i can save cca 500 extra bytes for my programs.

mihaigalos commented 6 years ago

Is smaller size not enough?

I remember looking at Micronucleus and being charmed by how simple it was. This ultimately led to the development of miniboot. I cannot say the same for optiboot.

NuclearPhoenixx commented 6 years ago

Smaller bootloader size is definitely a big plus, however, I know that optiboot works well with the ATmega328P, but the micronucleus build is too much work in progress for me to replace that for now.

mihaigalos commented 6 years ago

I ported the first beta to AtMega328p and @ AHorneffer matured the first working version. I don't know if it had been merged into master yet. If not, consult his local branch.

I am using Micronucleus on AtMega328p ever since. What do you mean work in progress? IMHO it's mature.

AHorneffer commented 5 years ago

@Phoenix1747 et al.: I use micronucleus for my projects because with only 5 simple additional components I can have a bootloader that allows me to update the program on my devices directly from my computer. Without having to fiddle around with an external programmer. (I like to power stuff from USB anyhow, so during development they are plugged into my computer anyhow.)

The ATmega328 support has been merged into the master branch, so I guess we can close this now.

drtrigon commented 2 years ago

I would like to do the same as @AHorneffer. I am building a minimal 328 board that actually has an oscillator and 2 caps only. Now I would like to use micronucleus as bootloader in order to do exactly the same; have only 5 additional components (3 res and 2 zehner) in order to program via USB (actually when working with 3.3V the 2 zehner can be omitted also ;).

Is there ANY documentation on how to wire the 328? What pins are used for USB D- and D+?

@AHorneffer: Do you have any documentation or notes? Some useful hints maybe?

mihaigalos commented 2 years ago

I saw the port and port bits being defined i.e. here, and schematics you can get on google. Example.

mihaigalos commented 2 years ago

BTW. I just remembered I always prefer leaving out the Zeners for test / non-production circuits, since the potential for overvoltage is pretty small. So I would only use the 68 ohm series resistors and the 1.5k ohm pullup. I like to add the Zeners to the board but not solder them.

Anyhow, for posterity, here is an example schematic from here: image

drtrigon commented 2 years ago

Thanks for the answer! I was able to make it work yesterday on my own. Here some hints for atmega328:

However there is one issue with the ext fuse. As per docu given here it should be ?????101 = 0xff.

  1. for me the first 5 bit have by avrdude been set to 0
  2. 101 at the end cannot give f

So for me it was set to 00000111 = 0x07. But if 101 is needed it should be 00000101 = 0x05. Can somebody comment on whats correct 101 or 111?

drtrigon commented 2 years ago

https://github.com/micronucleus/micronucleus/issues/249

DMaisterra commented 2 years ago

Hi. Sorrry if this have been outdated, dont find any info in other place... What definitions to the board you use? Been looking anywhere this is awsome!

mihaigalos commented 2 years ago

Not sure what you mean. I've built my own boards with AtMega328p. If you refer to which board you can select in i.e. Arduino, try to look for one with the above MCU.