New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

micronucleus for ATmega328P #107

Closed
AHorneffer opened this Issue Nov 17, 2017 · 21 comments

Comments

Projects
None yet
4 participants
@AHorneffer
Copy link
Contributor

AHorneffer commented Nov 17, 2017

[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:

  • When there is an active USB host present, then the bootloader doesn't start the application program. The status-LED of the bootloader just keeps blinking.
  • After loading one application program via USB, loading another one does not work: the loading process seems to go fine (the micronucleus command-line program sys it was successful) but the application doesn't work.
  • Even when loaded th first time after flashing the bootloader, some applications work (e.g. a simple "blink this LED") others don't.

I would also appreciate any suggestions how to debug this.

@AHorneffer

This comment has been minimized.

Copy link
Contributor Author

AHorneffer commented Nov 17, 2017

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:

  • with boot_page_write(currentAddress.w - 2); it is the last word
  • with boot_page_write(currentAddress.w - 8); it is the fourth last word (i.e. the two bytes starting from 8 bytes from the end of the page)
  • with boot_page_write(currentAddress.w - SPM_PAGESIZE); it is the first word in the page
  • with boot_page_write(currentAddress.w); it is also the first word in the page but - of course - the pages are all shifted by one.

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

This comment has been minimized.

Copy link
Contributor Author

AHorneffer commented Nov 20, 2017

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

This comment has been minimized.

Copy link

mihaigalos commented Dec 9, 2017

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

This comment has been minimized.

Copy link

mihaigalos commented Dec 9, 2017

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

This comment has been minimized.

Copy link
Contributor Author

AHorneffer commented Dec 9, 2017

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

This comment has been minimized.

Copy link

mihaigalos commented Dec 9, 2017

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

This comment has been minimized.

Copy link

mihaigalos commented Dec 9, 2017

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

This comment has been minimized.

Copy link
Contributor Author

AHorneffer commented Dec 9, 2017

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

This comment has been minimized.

Copy link
Contributor Author

AHorneffer commented Dec 9, 2017

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:

  • set the High-Fuse to "small bootloader" and "disable bootloader reset vector": hfuse:w:0xdf:m
  • set the bootloader size in the makefile to: 7A00

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

This comment has been minimized.

Copy link

mihaigalos commented Dec 9, 2017

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

@AHorneffer

This comment has been minimized.

Copy link
Contributor Author

AHorneffer commented Dec 9, 2017

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

This comment has been minimized.

Copy link

mihaigalos commented Dec 9, 2017

+1 for an extra precise answer.

AHorneffer added a commit to AHorneffer/micronucleus that referenced this issue Dec 10, 2017

change fuses and bootloader_address to allow larger applications
Coming from the discussion in micronucleus#107 (micronucleus#107),
it is possible to have part of the application inside the flash space that is defined as
bootloader flash by the fuse settings.
@AHorneffer

This comment has been minimized.

Copy link
Contributor Author

AHorneffer commented Dec 10, 2017

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

This comment has been minimized.

Copy link

mihaigalos commented Dec 11, 2017

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 pgm_read_.* to read them directly from the flash. See this for further reference.

@tykefcz

This comment has been minimized.

Copy link

tykefcz commented May 31, 2018

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:

  • can run "make t85_default" or other config name once and then run only "make upgrade" "make release" ... without CONFIG=xxx parameter
  • bootloader address computing at compile time depends on actual builded size.
  • working self upgrade for cpus with NRWW memory - spm instruction must be in address in NRWW memory to work
  • making upgrade not need any script language installed - gnu make v. 4.2 and newer is enough (on Windows and Linux).
  • use MN as library - saves cca 500 bytes flash (loader +128B app -650B), tested on HID and CDC projects on t85, mega8 and mega328 - tiny without xtal, mega with 16MHz xtal.
    Tykef.
@Phoenix1747

This comment has been minimized.

Copy link

Phoenix1747 commented Jun 1, 2018

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

This comment has been minimized.

Copy link

tykefcz commented Jun 1, 2018

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

This comment has been minimized.

Copy link

mihaigalos commented Jun 2, 2018

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.

@Phoenix1747

This comment has been minimized.

Copy link

Phoenix1747 commented Jun 2, 2018

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

This comment has been minimized.

Copy link

mihaigalos commented Jun 3, 2018

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

This comment has been minimized.

Copy link
Contributor Author

AHorneffer commented Dec 8, 2018

@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.

@AHorneffer AHorneffer closed this Dec 8, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment