-
Notifications
You must be signed in to change notification settings - Fork 134
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
Add fuses, calibration, sernum and tempsense memories to XMEGAs #1829
Add fuses, calibration, sernum and tempsense memories to XMEGAs #1829
Conversation
This commit makes the parent of XMEGA parts one of three common ones: - .xmega-ab for XMEGA-A or XMEGA-B parts - .xmega-cd for XMEGA-C or XMEGA-D parts - .xmega-e for XMEGA-E parts The commit does not change any feature of parts or memories. In order to prove this carry out before this commit $ avrdude -p */At >/tmp/1 After this commit, the following command should not have any output: $ avrdude -p */At | sort | comm -3 - /tmp/1 | grep -v family.common.values (The only feature changes in the .xmega-* prototypes.)
be79486
to
808457e
Compare
OK, backup and restore with the new memories seems to work, at least for the AVRISP mkII programmer, see below. Can someone check all other PDI programmers, please? JTAG and the like... @MCUdude? Also this PR adds sernum to m324pb, m328pb, t102 and t104. Can someone with an m328pb please check whether ALL backup and restore works? @mcuee? The new multi-memory files and -U command are really helpful to smoke out whether a programmer can deal with all memories. Backup
Restore
|
@mcuee @MCUdude @dl8dtl Although this PR deserves tests against more PDI programmer and tests for classic
This will smoke out programmers that cannot deal with some memories regardless. |
Yes I will test againt ATmega328PB over this weekend. |
Here is the test result for ATmega328PB. There is a problem with
|
For
|
Thanks for testing @mcuee . |
Although, on the surface of it, every entry of a classic part has been changed, and some radically so by different parenting, there are only subtle changes in AVRDUDE's internal representation: - 15 parts that did not have an io memory entry now have one - Some ISP opcodes were replaced with equivalent ones (x maps to 0) This has been checked using the development options, particularly -p*/At, before and after the changes. The benefit of the new avrdude.conf is that every (non-TPI) classic part is now derived from a common .classic or .classic-nocal stub. This enables putting a common prodsig and sernum memory there. Generally, parenting off now only happens between related chips. All in all, more systematic. And it's some 10% smaller, too.
All classic parts with calibration memory now export a prodsig and a sernum memory. I checked out a few parts and some seem to have data in |
PR seems to work great! It's interesting that the xmega
EDIT:
|
JTAG3 programmers are, however, able to read
|
Thanks for testing @MCUdude ! Yes, the tests reveal a problem with the code.
I need your help here. XMEGAs don't have the signature in the prodsig area; it's available in the MCU I/O registers (go figure) at a very different address from To cut a long short: I am a bit lost. Can you figure out which byte read code runs when you read from |
The programmer reads from the wrong memory area... |
I added a few print statements here and there, and here's the result. It doesn't print anything special when I'm running modified jtag3_read_byte()tatic int jtag3_read_byte(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
unsigned long addr, unsigned char * value) {
unsigned char cmd[12];
unsigned char *resp, *cache_ptr = NULL;
int status, unsupp = 0;
unsigned long paddr = 0UL, *paddr_ptr = NULL;
unsigned int pagesize = 0;
pmsg_notice2("jtag3_read_byte(.., %s, 0x%lx, ...)\n", mem->desc, addr);
paddr = jtag3_memaddr(pgm, p, mem, addr);
if (paddr != addr)
imsg_debug("addr 0x%lx mapped to address 0x%lx\n", addr, paddr);
paddr = 0;
if (mem->size < 1) {
pmsg_error("cannot read byte from %s %s owing to its size %d\n", p->desc, mem->desc, mem->size);
return -1;
}
if (addr >= (unsigned long) mem->size) {
pmsg_error("cannot read byte from %s %s as address 0x%04lx outside range [0, 0x%04x]\n",
p->desc, mem->desc, addr, mem->size-1);
return -1;
}
if (!(pgm->flag & PGM_FL_IS_DW))
if ((status = jtag3_program_enable(pgm)) < 0)
return status;
cmd[0] = SCOPE_AVR;
cmd[1] = CMD3_READ_MEMORY;
cmd[2] = 0;
cmd[3] = p->prog_modes & (PM_PDI | PM_UPDI)? MTYPE_FLASH: MTYPE_FLASH_PAGE;
if (mem_is_in_flash(mem)) {
addr += mem->offset & (512 * 1024 - 1); /* max 512 KiB flash @@@ could be max 8M */
pagesize = PDATA(pgm)->flash_pagesize;
paddr = addr & ~(pagesize - 1);
paddr_ptr = &PDATA(pgm)->flash_pageaddr;
cache_ptr = PDATA(pgm)->flash_pagecache;
msg_info("mem_is_in_flash\n");
} else if (mem_is_eeprom(mem)) {
if ( (pgm->flag & PGM_FL_IS_DW) || (p->prog_modes & PM_PDI) || (p->prog_modes & PM_UPDI) ) {
cmd[3] = MTYPE_EEPROM;
msg_info("mem_is_eeprom, MTYPE_EEPROM\n");
} else {
cmd[3] = MTYPE_EEPROM_PAGE;
}
pagesize = mem->page_size;
paddr = addr & ~(pagesize - 1);
paddr_ptr = &PDATA(pgm)->eeprom_pageaddr;
cache_ptr = PDATA(pgm)->eeprom_pagecache;
} else if (mem_is_a_fuse(mem) || mem_is_fuses(mem)) {
cmd[3] = MTYPE_FUSE_BITS;
msg_info("mem_is_a_fuse\n");
if(!(p->prog_modes & PM_UPDI) && mem_is_a_fuse(mem))
addr = mem_fuse_offset(mem);
if (pgm->flag & PGM_FL_IS_DW)
unsupp = 1;
} else if (mem_is_lock(mem)) {
msg_info("mem_is_lock\n");
cmd[3] = MTYPE_LOCK_BITS;
if (pgm->flag & PGM_FL_IS_DW)
unsupp = 1;
} else if (mem_is_userrow(mem)) {
msg_info("mem_is_userrow\n");
cmd[3] = MTYPE_USERSIG;
} else if (mem_is_sigrow(mem)) {
if (p->prog_modes & (PM_PDI | PM_UPDI)) {
cmd[3] = MTYPE_PRODSIG;
msg_info("mem_is_sigrow\n");
} else {
cmd[3] = addr&1? MTYPE_OSCCAL_BYTE: MTYPE_SIGN_JTAG;
addr /= 2;
if (pgm->flag & PGM_FL_IS_DW)
unsupp = 1;
}
} else if ((p->prog_modes & PM_Classic) && mem_is_calibration(mem)) { // Classic part calibration
cmd[3] = MTYPE_OSCCAL_BYTE;
if (pgm->flag & PGM_FL_IS_DW)
unsupp = 1;
} else if (mem_is_io(mem) || mem_is_sram(mem)) {
cmd[3] = MTYPE_SRAM;
msg_info("mem_is_io || mem_is_sram\n");
} else if (mem_is_sib(mem)) {
if(addr >= AVR_SIBLEN) {
pmsg_error("cannot read byte from %s sib as address 0x%04lx outside range [0, 0x%04x]\n",
p->desc, addr, AVR_SIBLEN-1);
return -1;
}
if(!*PDATA(pgm)->sib_string) {
pmsg_error("cannot read byte from %s sib as memory not initialised\n", p->desc);
return -1;
}
*value = PDATA(pgm)->sib_string[addr];
return 0;
} else if (mem_is_signature(mem)) {
msg_info("mem_is_signature\n");
cmd[3] = MTYPE_SIGN_JTAG;
/*
* dW can read out the signature on JTAGICE3, but only allows
* for a full three-byte read. We cache them in a local
* variable to avoid multiple reads. This optimization does not
* harm for other connection types either.
*/
u32_to_b4(cmd + 8, 3);
u32_to_b4(cmd + 4, jtag3_memaddr(pgm, p, mem, addr));
if (addr == 0) {
if ((status = jtag3_command(pgm, cmd, 12, &resp, "read memory")) < 0)
return status;
PDATA(pgm)->signature_cache[0] = resp[4];
PDATA(pgm)->signature_cache[1] = resp[5];
*value = resp[3];
mmt_free(resp);
return 0;
} else if (addr <= 2) {
*value = PDATA(pgm)->signature_cache[addr - 1];
return 0;
} else {
/* should not happen */
msg_error("address out of range for signature memory: %lu\n", addr);
return -1;
}
} else if(mem_is_in_sigrow(mem)) { // sigrow submemories but not signature nor sigrow itself
cmd[3] = (p->prog_modes & PM_PDI)? MTYPE_SIGN_JTAG: MTYPE_PRODSIG;
AVRMEM *sigrow = avr_locate_sigrow(p);
if(sigrow)
addr += mem->offset - sigrow->offset; // Adjust offset for parent memory
} else {
pmsg_error("unknown memory %s\n", mem->desc);
return -1;
}
/*
* If the respective memory area is not supported under debugWire,
* leave here.
*/
if (unsupp) {
*value = 42;
return -1;
}
/*
* To improve the read speed, we used paged reads for flash and
* EEPROM, and cache the results in a page cache.
*
* Page cache validation is based on "{flash,eeprom}_pageaddr"
* (holding the base address of the most recent cache fill
* operation). This variable is set to (unsigned long)-1L when the
* cache needs to be invalidated.
*/
if (pagesize && paddr == *paddr_ptr) {
*value = cache_ptr[addr & (pagesize - 1)];
return 0;
}
if (pagesize) {
u32_to_b4(cmd + 8, pagesize);
u32_to_b4(cmd + 4, jtag3_memaddr(pgm, p, mem, paddr));
} else {
u32_to_b4(cmd + 8, 1);
u32_to_b4(cmd + 4, jtag3_memaddr(pgm, p, mem, addr));
}
if ((status = jtag3_command(pgm, cmd, 12, &resp, "read memory")) < 0)
return status;
if (resp[1] != RSP3_DATA ||
status < (int) (pagesize? pagesize: 1) + 4) {
pmsg_error("wrong/short reply to read memory command\n");
mmt_free(resp);
return -1;
}
if (pagesize) {
*paddr_ptr = paddr;
memcpy(cache_ptr, resp + 3, pagesize);
*value = cache_ptr[addr & (pagesize - 1)];
} else
*value = resp[3];
mmt_free(resp);
return 0;
}
|
Ignore my previous post. Here's the output when reading sernum:
Modified jtag3_read_byte()static int jtag3_read_byte(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
unsigned long addr, unsigned char * value) {
unsigned char cmd[12];
unsigned char *resp, *cache_ptr = NULL;
int status, unsupp = 0;
unsigned long paddr = 0UL, *paddr_ptr = NULL;
unsigned int pagesize = 0;
pmsg_notice2("jtag3_read_byte(.., %s, 0x%lx, ...)\n", mem->desc, addr);
paddr = jtag3_memaddr(pgm, p, mem, addr);
if (paddr != addr)
imsg_debug("addr 0x%lx mapped to address 0x%lx\n", addr, paddr);
paddr = 0;
if (mem->size < 1) {
pmsg_error("cannot read byte from %s %s owing to its size %d\n", p->desc, mem->desc, mem->size);
return -1;
}
if (addr >= (unsigned long) mem->size) {
pmsg_error("cannot read byte from %s %s as address 0x%04lx outside range [0, 0x%04x]\n",
p->desc, mem->desc, addr, mem->size-1);
return -1;
}
if (!(pgm->flag & PGM_FL_IS_DW))
if ((status = jtag3_program_enable(pgm)) < 0)
return status;
cmd[0] = SCOPE_AVR;
cmd[1] = CMD3_READ_MEMORY;
cmd[2] = 0;
cmd[3] = p->prog_modes & (PM_PDI | PM_UPDI)? MTYPE_FLASH: MTYPE_FLASH_PAGE;
if (mem_is_in_flash(mem)) {
addr += mem->offset & (512 * 1024 - 1); /* max 512 KiB flash @@@ could be max 8M */
pagesize = PDATA(pgm)->flash_pagesize;
paddr = addr & ~(pagesize - 1);
paddr_ptr = &PDATA(pgm)->flash_pageaddr;
cache_ptr = PDATA(pgm)->flash_pagecache;
msg_info("mem_is_in_flash\n");
} else if (mem_is_eeprom(mem)) {
if ( (pgm->flag & PGM_FL_IS_DW) || (p->prog_modes & PM_PDI) || (p->prog_modes & PM_UPDI) ) {
cmd[3] = MTYPE_EEPROM;
msg_info("mem_is_eeprom, MTYPE_EEPROM\n");
} else {
cmd[3] = MTYPE_EEPROM_PAGE;
}
pagesize = mem->page_size;
paddr = addr & ~(pagesize - 1);
paddr_ptr = &PDATA(pgm)->eeprom_pageaddr;
cache_ptr = PDATA(pgm)->eeprom_pagecache;
} else if (mem_is_a_fuse(mem) || mem_is_fuses(mem)) {
cmd[3] = MTYPE_FUSE_BITS;
msg_info("mem_is_a_fuse\n");
if(!(p->prog_modes & PM_UPDI) && mem_is_a_fuse(mem))
addr = mem_fuse_offset(mem);
if (pgm->flag & PGM_FL_IS_DW)
unsupp = 1;
} else if (mem_is_lock(mem)) {
msg_info("mem_is_lock\n");
cmd[3] = MTYPE_LOCK_BITS;
if (pgm->flag & PGM_FL_IS_DW)
unsupp = 1;
} else if (mem_is_userrow(mem)) {
msg_info("mem_is_userrow\n");
cmd[3] = MTYPE_USERSIG;
} else if (mem_is_sigrow(mem)) {
if (p->prog_modes & (PM_PDI | PM_UPDI)) {
cmd[3] = MTYPE_PRODSIG;
msg_info("mem_is_sigrow\n");
} else {
cmd[3] = addr&1? MTYPE_OSCCAL_BYTE: MTYPE_SIGN_JTAG;
addr /= 2;
if (pgm->flag & PGM_FL_IS_DW)
unsupp = 1;
}
} else if ((p->prog_modes & PM_Classic) && mem_is_calibration(mem)) { // Classic part calibration
cmd[3] = MTYPE_OSCCAL_BYTE;
if (pgm->flag & PGM_FL_IS_DW)
unsupp = 1;
} else if (mem_is_io(mem) || mem_is_sram(mem)) {
cmd[3] = MTYPE_SRAM;
msg_info("mem_is_io || mem_is_sram\n");
} else if (mem_is_sib(mem)) {
if(addr >= AVR_SIBLEN) {
pmsg_error("cannot read byte from %s sib as address 0x%04lx outside range [0, 0x%04x]\n",
p->desc, addr, AVR_SIBLEN-1);
return -1;
}
if(!*PDATA(pgm)->sib_string) {
pmsg_error("cannot read byte from %s sib as memory not initialised\n", p->desc);
return -1;
}
*value = PDATA(pgm)->sib_string[addr];
return 0;
} else if (mem_is_signature(mem)) {
msg_info("mem_is_signature\n");
cmd[3] = MTYPE_SIGN_JTAG;
/*
* dW can read out the signature on JTAGICE3, but only allows
* for a full three-byte read. We cache them in a local
* variable to avoid multiple reads. This optimization does not
* harm for other connection types either.
*/
u32_to_b4(cmd + 8, 3);
u32_to_b4(cmd + 4, jtag3_memaddr(pgm, p, mem, addr));
if (addr == 0) {
if ((status = jtag3_command(pgm, cmd, 12, &resp, "read memory")) < 0)
return status;
PDATA(pgm)->signature_cache[0] = resp[4];
PDATA(pgm)->signature_cache[1] = resp[5];
*value = resp[3];
mmt_free(resp);
return 0;
} else if (addr <= 2) {
*value = PDATA(pgm)->signature_cache[addr - 1];
return 0;
} else {
/* should not happen */
msg_error("address out of range for signature memory: %lu\n", addr);
return -1;
}
} else if(mem_is_in_sigrow(mem)) { // sigrow submemories but not signature nor sigrow itself
cmd[3] = (p->prog_modes & PM_PDI)? MTYPE_SIGN_JTAG: MTYPE_PRODSIG;
AVRMEM *sigrow = avr_locate_sigrow(p);
if(sigrow)
addr += mem->offset - sigrow->offset; // Adjust offset for parent memory
msg_info(" mem_is_in_sigrow(). addr: 0x%lx, mem->offset: 0x%lx, sigrow->offset: 0x%lx\n", addr, mem->offset, sigrow->offset);
} else {
pmsg_error("unknown memory %s\n", mem->desc);
return -1;
}
/*
* If the respective memory area is not supported under debugWire,
* leave here.
*/
if (unsupp) {
*value = 42;
return -1;
}
/*
* To improve the read speed, we used paged reads for flash and
* EEPROM, and cache the results in a page cache.
*
* Page cache validation is based on "{flash,eeprom}_pageaddr"
* (holding the base address of the most recent cache fill
* operation). This variable is set to (unsigned long)-1L when the
* cache needs to be invalidated.
*/
if (pagesize && paddr == *paddr_ptr) {
*value = cache_ptr[addr & (pagesize - 1)];
return 0;
}
if (pagesize) {
u32_to_b4(cmd + 8, pagesize);
u32_to_b4(cmd + 4, jtag3_memaddr(pgm, p, mem, paddr));
} else {
u32_to_b4(cmd + 8, 1);
u32_to_b4(cmd + 4, jtag3_memaddr(pgm, p, mem, addr));
}
if ((status = jtag3_command(pgm, cmd, 12, &resp, "read memory")) < 0)
return status;
if (resp[1] != RSP3_DATA ||
status < (int) (pagesize? pagesize: 1) + 4) {
pmsg_error("wrong/short reply to read memory command\n");
mmt_free(resp);
return -1;
}
if (pagesize) {
*paddr_ptr = paddr;
memcpy(cache_ptr, resp + 3, pagesize);
*value = cache_ptr[addr & (pagesize - 1)];
} else
*value = resp[3];
mmt_free(resp);
return 0;
} |
@MCUdude Brilliant! I think this gets us closer: check out the latest commit |
There we go!
|
Thanks! @MCUdude. The jtag3.c code is a bit fickle as it morphs depending on the parts, pgm i/f etc. Would you be able to give the jtag3.c programmers a thorough spin re TPI, other classic parts, PDI and UPDI parts and see whether it reads the correct memory regions? There may be another, similar treadmine lurking! |
TPI (ATtiny9 + PICkit4):
UPDI (AVR64DU28 + PICKit4):
It seems like tempsense and sernum are broken for UPDI parts. They did work before. EDIT: I can confirm that this PR breaks tempsense and sernum. Git main works fine. |
FIY I've been back from vacation for 24 hours, but we're leaving again tomorrow morning, and will be away for five-six days. Will read emails, but don't count on me when it comes to testing until I'm back home. |
Enjoy your trip(s). I think I've solved the regression re UPDI sernum etc. There is more to do: UPDI's |
No more mismatch of prodsig for
|
This PR works fine with
|
For
|
Somehow I can not get an AVRISP mkII clone to work properly with ATxmega32A4U. This may not be related to this PR though. I will try other clones.
On the other hand, no issue if I just read back the eeprom or flash.
|
Looks like a temporary USB communication issue. Now it is okay. The reason is probably because of using jumper wires from the programmer to the ATxmega32AU4 breakout board.
|
Thanks @mcuee for testing. Any chance you have an ATtiny102 or 104 that you can test for the Also, for future tests, I now recommend a slightly changed line:
Note the |
Unfortunately I do not have good setup to test TPI parts in general. |
... so that XMEGAs now also have the
fuses
memory encompassing allfuseX
memories just like the modern AVRs. Also provides the sub-memories inprodsig
. As bonus addssernum
to those 4 classic parts that haveprodisig
.Perhaps neater to test this PR after multi-memory files have been tested, so can check out
avrdude -U ALL:r:backup
and thenavrdude -U ALL:w:backup
. As always be prepared to lose chips when testing without having carefully checked what the command does under-c dryrun
. However, only testing with a real programmer will reveal whether the programmer understands the new memories. Backing up and restoring with-U ALL
is a great way of checking out how well a pogrammer implementation deals with exotic memories.