Submitted by Jan Egil Ruud <janegil.ruud@microchip.com>
patch #9507: Fix UPDI chip erase * libavrdude.h (PROGRAMMER): add unlock method * avr.c (avr_unlock): Generic unlock function * jtag3.c: Implement unlock feature; avoid calling jtag3_edbg_prepare() and jtag3_edbg_signoff() on XplainedMini boards to work around a bug in early firmware versions; implement "userrow" memory region * main.c: Call avr_unlock() rather than avr_chip_erase() when encountering a locked UPDI chip git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@1417 81a1dc3b-b13d-400b-aceb-764788c761c2
This commit is contained in:
parent
7eb498992c
commit
c3ec8d5229
13
ChangeLog
13
ChangeLog
|
@ -1,3 +1,16 @@
|
||||||
|
2018-01-16 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
|
||||||
|
|
||||||
|
Submitted by Jan Egil Ruud <janegil.ruud@microchip.com>
|
||||||
|
patch #9507: Fix UPDI chip erase
|
||||||
|
* libavrdude.h (PROGRAMMER): add unlock method
|
||||||
|
* avr.c (avr_unlock): Generic unlock function
|
||||||
|
* jtag3.c: Implement unlock feature; avoid calling
|
||||||
|
jtag3_edbg_prepare() and jtag3_edbg_signoff() on XplainedMini
|
||||||
|
boards to work around a bug in early firmware versions;
|
||||||
|
implement "userrow" memory region
|
||||||
|
* main.c: Call avr_unlock() rather than avr_chip_erase() when
|
||||||
|
encountering a locked UPDI chip
|
||||||
|
|
||||||
2018-01-16 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
|
2018-01-16 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
|
||||||
|
|
||||||
Submitted by Nicolas BRULEZ
|
Submitted by Nicolas BRULEZ
|
||||||
|
|
10
avr.c
10
avr.c
|
@ -1195,6 +1195,16 @@ int avr_chip_erase(PROGRAMMER * pgm, AVRPART * p)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int avr_unlock(PROGRAMMER * pgm, AVRPART * p)
|
||||||
|
{
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
if (pgm->unlock)
|
||||||
|
rc = pgm->unlock(pgm, p);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Report the progress of a read or write operation from/to the
|
* Report the progress of a read or write operation from/to the
|
||||||
* device.
|
* device.
|
||||||
|
|
84
jtag3.c
84
jtag3.c
|
@ -864,9 +864,14 @@ int jtag3_getsync(PROGRAMMER * pgm, int mode) {
|
||||||
|
|
||||||
avrdude_message(MSG_DEBUG, "%s: jtag3_getsync()\n", progname);
|
avrdude_message(MSG_DEBUG, "%s: jtag3_getsync()\n", progname);
|
||||||
|
|
||||||
|
/* XplainedMini boards do not need this, and early revisions had a
|
||||||
|
* firmware bug where they complained about it. */
|
||||||
if (pgm->flag & PGM_FL_IS_EDBG) {
|
if (pgm->flag & PGM_FL_IS_EDBG) {
|
||||||
if (jtag3_edbg_prepare(pgm) < 0)
|
if (strcmp(pgm->id, "xplainedmini_updi") != 0) {
|
||||||
return -1;
|
if (jtag3_edbg_prepare(pgm) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the sign-on information. */
|
/* Get the sign-on information. */
|
||||||
|
@ -903,9 +908,9 @@ static int jtag3_chip_erase(PROGRAMMER * pgm, AVRPART * p)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* UPDI 'chip erase' -> 'enter progmode' with chip erase key
|
* UPDI 'unlock' -> 'enter progmode' with chip erase key
|
||||||
*/
|
*/
|
||||||
static int jtag3_chip_erase_updi(PROGRAMMER * pgm, AVRPART * p)
|
static int jtag3_unlock_erase_key(PROGRAMMER * pgm, AVRPART * p)
|
||||||
{
|
{
|
||||||
unsigned char buf[8], *resp;
|
unsigned char buf[8], *resp;
|
||||||
|
|
||||||
|
@ -1573,8 +1578,13 @@ void jtag3_close(PROGRAMMER * pgm)
|
||||||
if (jtag3_command(pgm, buf, 4, &resp, "sign-off") >= 0)
|
if (jtag3_command(pgm, buf, 4, &resp, "sign-off") >= 0)
|
||||||
free(resp);
|
free(resp);
|
||||||
|
|
||||||
if (pgm->flag & PGM_FL_IS_EDBG)
|
/* XplainedMini boards do not need this, and early revisions had a
|
||||||
jtag3_edbg_signoff(pgm);
|
* firmware bug where they complained about it. */
|
||||||
|
if (pgm->flag & PGM_FL_IS_EDBG) {
|
||||||
|
if (strcmp(pgm->id, "xplainedmini_updi") != 0) {
|
||||||
|
jtag3_edbg_signoff(pgm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
serial_close(&pgm->fd);
|
serial_close(&pgm->fd);
|
||||||
pgm->fd.ifd = -1;
|
pgm->fd.ifd = -1;
|
||||||
|
@ -1636,8 +1646,13 @@ static int jtag3_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
|
||||||
int status, dynamic_memtype = 0;
|
int status, dynamic_memtype = 0;
|
||||||
long otimeout = serial_recv_timeout;
|
long otimeout = serial_recv_timeout;
|
||||||
|
|
||||||
avrdude_message(MSG_NOTICE2, "%s: jtag3_paged_write(.., %s, %d, %d)\n",
|
avrdude_message(MSG_NOTICE2, "%s: jtag3_paged_write(.., %s, %d, 0x%lx, %d)\n",
|
||||||
progname, m->desc, page_size, n_bytes);
|
progname, m->desc, page_size, addr, n_bytes);
|
||||||
|
|
||||||
|
block_size = jtag3_memaddr(pgm, p, m, addr);
|
||||||
|
if(block_size != addr)
|
||||||
|
avrdude_message(MSG_NOTICE2, " mapped to address: 0x%lx\n", block_size);
|
||||||
|
block_size = 0;
|
||||||
|
|
||||||
if (!(pgm->flag & PGM_FL_IS_DW) && jtag3_program_enable(pgm) < 0)
|
if (!(pgm->flag & PGM_FL_IS_DW) && jtag3_program_enable(pgm) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1679,6 +1694,8 @@ static int jtag3_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
|
||||||
PDATA(pgm)->eeprom_pageaddr = (unsigned long)-1L;
|
PDATA(pgm)->eeprom_pageaddr = (unsigned long)-1L;
|
||||||
} else if ( ( strcmp(m->desc, "usersig") == 0 ) ) {
|
} else if ( ( strcmp(m->desc, "usersig") == 0 ) ) {
|
||||||
cmd[3] = MTYPE_USERSIG;
|
cmd[3] = MTYPE_USERSIG;
|
||||||
|
} else if ( ( strcmp(m->desc, "userrow") == 0 ) ) {
|
||||||
|
cmd[3] = MTYPE_USERSIG;
|
||||||
} else if ( ( strcmp(m->desc, "boot") == 0 ) ) {
|
} else if ( ( strcmp(m->desc, "boot") == 0 ) ) {
|
||||||
cmd[3] = MTYPE_BOOT_FLASH;
|
cmd[3] = MTYPE_BOOT_FLASH;
|
||||||
} else if ( p->flags & AVRPART_HAS_PDI || p->flags & AVRPART_HAS_UPDI ) {
|
} else if ( p->flags & AVRPART_HAS_PDI || p->flags & AVRPART_HAS_UPDI ) {
|
||||||
|
@ -1740,8 +1757,13 @@ static int jtag3_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
|
||||||
int status, dynamic_memtype = 0;
|
int status, dynamic_memtype = 0;
|
||||||
long otimeout = serial_recv_timeout;
|
long otimeout = serial_recv_timeout;
|
||||||
|
|
||||||
avrdude_message(MSG_NOTICE2, "%s: jtag3_paged_load(.., %s, %d, %d)\n",
|
avrdude_message(MSG_NOTICE2, "%s: jtag3_paged_load(.., %s, %d, 0x%lx, %d)\n",
|
||||||
progname, m->desc, page_size, n_bytes);
|
progname, m->desc, page_size, addr, n_bytes);
|
||||||
|
|
||||||
|
block_size = jtag3_memaddr(pgm, p, m, addr);
|
||||||
|
if(block_size != addr)
|
||||||
|
avrdude_message(MSG_NOTICE2, " mapped to address: 0x%lx\n", block_size);
|
||||||
|
block_size = 0;
|
||||||
|
|
||||||
if (!(pgm->flag & PGM_FL_IS_DW) && jtag3_program_enable(pgm) < 0)
|
if (!(pgm->flag & PGM_FL_IS_DW) && jtag3_program_enable(pgm) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1765,6 +1787,8 @@ static int jtag3_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
|
||||||
cmd[3] = MTYPE_PRODSIG;
|
cmd[3] = MTYPE_PRODSIG;
|
||||||
} else if ( ( strcmp(m->desc, "usersig") == 0 ) ) {
|
} else if ( ( strcmp(m->desc, "usersig") == 0 ) ) {
|
||||||
cmd[3] = MTYPE_USERSIG;
|
cmd[3] = MTYPE_USERSIG;
|
||||||
|
} else if ( ( strcmp(m->desc, "userrow") == 0 ) ) {
|
||||||
|
cmd[3] = MTYPE_USERSIG;
|
||||||
} else if ( ( strcmp(m->desc, "boot") == 0 ) ) {
|
} else if ( ( strcmp(m->desc, "boot") == 0 ) ) {
|
||||||
cmd[3] = MTYPE_BOOT_FLASH;
|
cmd[3] = MTYPE_BOOT_FLASH;
|
||||||
} else if ( p->flags & AVRPART_HAS_PDI || p->flags & AVRPART_HAS_UPDI ) {
|
} else if ( p->flags & AVRPART_HAS_PDI || p->flags & AVRPART_HAS_UPDI ) {
|
||||||
|
@ -1819,6 +1843,11 @@ static int jtag3_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||||
avrdude_message(MSG_NOTICE2, "%s: jtag3_read_byte(.., %s, 0x%lx, ...)\n",
|
avrdude_message(MSG_NOTICE2, "%s: jtag3_read_byte(.., %s, 0x%lx, ...)\n",
|
||||||
progname, mem->desc, addr);
|
progname, mem->desc, addr);
|
||||||
|
|
||||||
|
paddr = jtag3_memaddr(pgm, p, mem, addr);
|
||||||
|
if(paddr != addr)
|
||||||
|
avrdude_message(MSG_NOTICE2, " mapped to address: 0x%lx\n", paddr);
|
||||||
|
paddr = 0;
|
||||||
|
|
||||||
if (!(pgm->flag & PGM_FL_IS_DW))
|
if (!(pgm->flag & PGM_FL_IS_DW))
|
||||||
if ((status = jtag3_program_enable(pgm)) < 0)
|
if ((status = jtag3_program_enable(pgm)) < 0)
|
||||||
return status;
|
return status;
|
||||||
|
@ -1828,8 +1857,6 @@ static int jtag3_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||||
cmd[2] = 0;
|
cmd[2] = 0;
|
||||||
|
|
||||||
cmd[3] = ( p->flags & AVRPART_HAS_PDI || p->flags & AVRPART_HAS_UPDI ) ? MTYPE_FLASH : MTYPE_FLASH_PAGE;
|
cmd[3] = ( p->flags & AVRPART_HAS_PDI || p->flags & AVRPART_HAS_UPDI ) ? MTYPE_FLASH : MTYPE_FLASH_PAGE;
|
||||||
if (p->flags & AVRPART_HAS_UPDI)
|
|
||||||
addr += mem->offset;
|
|
||||||
if (strcmp(mem->desc, "flash") == 0 ||
|
if (strcmp(mem->desc, "flash") == 0 ||
|
||||||
strcmp(mem->desc, "application") == 0 ||
|
strcmp(mem->desc, "application") == 0 ||
|
||||||
strcmp(mem->desc, "apptable") == 0 ||
|
strcmp(mem->desc, "apptable") == 0 ||
|
||||||
|
@ -1874,6 +1901,8 @@ static int jtag3_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||||
addr = mem->offset & 7;
|
addr = mem->offset & 7;
|
||||||
} else if (strcmp(mem->desc, "usersig") == 0) {
|
} else if (strcmp(mem->desc, "usersig") == 0) {
|
||||||
cmd[3] = MTYPE_USERSIG;
|
cmd[3] = MTYPE_USERSIG;
|
||||||
|
} else if (strcmp(mem->desc, "userrow") == 0) {
|
||||||
|
cmd[3] = MTYPE_USERSIG;
|
||||||
} else if (strcmp(mem->desc, "prodsig") == 0) {
|
} else if (strcmp(mem->desc, "prodsig") == 0) {
|
||||||
cmd[3] = MTYPE_PRODSIG;
|
cmd[3] = MTYPE_PRODSIG;
|
||||||
} else if (strcmp(mem->desc, "calibration") == 0) {
|
} else if (strcmp(mem->desc, "calibration") == 0) {
|
||||||
|
@ -1892,11 +1921,7 @@ static int jtag3_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||||
* harm for other connection types either.
|
* harm for other connection types either.
|
||||||
*/
|
*/
|
||||||
u32_to_b4(cmd + 8, 3);
|
u32_to_b4(cmd + 8, 3);
|
||||||
u32_to_b4(cmd + 4, mem->offset);
|
u32_to_b4(cmd + 4, jtag3_memaddr(pgm, p, mem, addr));
|
||||||
|
|
||||||
if (p->flags & AVRPART_HAS_UPDI){
|
|
||||||
addr -= mem->offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addr == 0) {
|
if (addr == 0) {
|
||||||
if ((status = jtag3_command(pgm, cmd, 12, &resp, "read memory")) < 0)
|
if ((status = jtag3_command(pgm, cmd, 12, &resp, "read memory")) < 0)
|
||||||
|
@ -1942,10 +1967,11 @@ static int jtag3_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||||
|
|
||||||
if (pagesize) {
|
if (pagesize) {
|
||||||
u32_to_b4(cmd + 8, pagesize);
|
u32_to_b4(cmd + 8, pagesize);
|
||||||
u32_to_b4(cmd + 4, paddr);
|
u32_to_b4(cmd + 4, jtag3_memaddr(pgm, p, mem, paddr));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
u32_to_b4(cmd + 8, 1);
|
u32_to_b4(cmd + 8, 1);
|
||||||
u32_to_b4(cmd + 4, addr);
|
u32_to_b4(cmd + 4, jtag3_memaddr(pgm, p, mem, addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((status = jtag3_command(pgm, cmd, 12, &resp, "read memory")) < 0)
|
if ((status = jtag3_command(pgm, cmd, 12, &resp, "read memory")) < 0)
|
||||||
|
@ -1978,16 +2004,19 @@ static int jtag3_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||||
unsigned char *cache_ptr = 0;
|
unsigned char *cache_ptr = 0;
|
||||||
int status, unsupp = 0;
|
int status, unsupp = 0;
|
||||||
unsigned int pagesize = 0;
|
unsigned int pagesize = 0;
|
||||||
|
unsigned long mapped_addr;
|
||||||
|
|
||||||
avrdude_message(MSG_NOTICE2, "%s: jtag3_write_byte(.., %s, 0x%lx, ...)\n",
|
avrdude_message(MSG_NOTICE2, "%s: jtag3_write_byte(.., %s, 0x%lx, ...)\n",
|
||||||
progname, mem->desc, addr);
|
progname, mem->desc, addr);
|
||||||
|
|
||||||
|
mapped_addr = jtag3_memaddr(pgm, p, mem, addr);
|
||||||
|
if(mapped_addr != addr)
|
||||||
|
avrdude_message(MSG_NOTICE2, " mapped to address: 0x%lx\n", mapped_addr);
|
||||||
|
|
||||||
cmd[0] = SCOPE_AVR;
|
cmd[0] = SCOPE_AVR;
|
||||||
cmd[1] = CMD3_WRITE_MEMORY;
|
cmd[1] = CMD3_WRITE_MEMORY;
|
||||||
cmd[2] = 0;
|
cmd[2] = 0;
|
||||||
cmd[3] = ( p->flags & AVRPART_HAS_PDI || p->flags & AVRPART_HAS_UPDI ) ? MTYPE_FLASH : MTYPE_SPM;
|
cmd[3] = ( p->flags & AVRPART_HAS_PDI || p->flags & AVRPART_HAS_UPDI ) ? MTYPE_FLASH : MTYPE_SPM;
|
||||||
if (p->flags & AVRPART_HAS_UPDI)
|
|
||||||
addr += mem->offset;
|
|
||||||
if (strcmp(mem->desc, "flash") == 0) {
|
if (strcmp(mem->desc, "flash") == 0) {
|
||||||
cache_ptr = PDATA(pgm)->flash_pagecache;
|
cache_ptr = PDATA(pgm)->flash_pagecache;
|
||||||
pagesize = PDATA(pgm)->flash_pagesize;
|
pagesize = PDATA(pgm)->flash_pagesize;
|
||||||
|
@ -2070,7 +2099,7 @@ static int jtag3_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
u32_to_b4(cmd + 8, 1);
|
u32_to_b4(cmd + 8, 1);
|
||||||
u32_to_b4(cmd + 4, addr);
|
u32_to_b4(cmd + 4, jtag3_memaddr(pgm, p, mem, addr));
|
||||||
cmd[12] = 0;
|
cmd[12] = 0;
|
||||||
cmd[13] = data;
|
cmd[13] = data;
|
||||||
|
|
||||||
|
@ -2325,7 +2354,13 @@ static unsigned int jtag3_memaddr(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, uns
|
||||||
* Non-Xmega device.
|
* Non-Xmega device.
|
||||||
*/
|
*/
|
||||||
if (p->flags & AVRPART_HAS_UPDI) {
|
if (p->flags & AVRPART_HAS_UPDI) {
|
||||||
if (strcmp(m->desc, "flash") != 0) {
|
if (strcmp(m->desc, "fuses") == 0) {
|
||||||
|
addr += m->offset;
|
||||||
|
}
|
||||||
|
else if (strncmp(m->desc, "fuse", strlen("fuse")) == 0) {
|
||||||
|
addr = m->offset;
|
||||||
|
}
|
||||||
|
else if (strcmp(m->desc, "flash") != 0) {
|
||||||
addr += m->offset;
|
addr += m->offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2448,7 +2483,7 @@ void jtag3_updi_initpgm(PROGRAMMER * pgm)
|
||||||
pgm->enable = jtag3_enable;
|
pgm->enable = jtag3_enable;
|
||||||
pgm->disable = jtag3_disable;
|
pgm->disable = jtag3_disable;
|
||||||
pgm->program_enable = jtag3_program_enable_dummy;
|
pgm->program_enable = jtag3_program_enable_dummy;
|
||||||
pgm->chip_erase = jtag3_chip_erase_updi;
|
pgm->chip_erase = jtag3_chip_erase;
|
||||||
pgm->open = jtag3_open_updi;
|
pgm->open = jtag3_open_updi;
|
||||||
pgm->close = jtag3_close;
|
pgm->close = jtag3_close;
|
||||||
pgm->read_byte = jtag3_read_byte;
|
pgm->read_byte = jtag3_read_byte;
|
||||||
|
@ -2466,6 +2501,7 @@ void jtag3_updi_initpgm(PROGRAMMER * pgm)
|
||||||
pgm->teardown = jtag3_teardown;
|
pgm->teardown = jtag3_teardown;
|
||||||
pgm->page_size = 256;
|
pgm->page_size = 256;
|
||||||
pgm->flag = PGM_FL_IS_UPDI;
|
pgm->flag = PGM_FL_IS_UPDI;
|
||||||
|
pgm->unlock = jtag3_unlock_erase_key;
|
||||||
pgm->read_sib = jtag3_read_sib;
|
pgm->read_sib = jtag3_read_sib;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -647,6 +647,7 @@ typedef struct programmer_t {
|
||||||
void (*powerdown) (struct programmer_t * pgm);
|
void (*powerdown) (struct programmer_t * pgm);
|
||||||
int (*program_enable) (struct programmer_t * pgm, AVRPART * p);
|
int (*program_enable) (struct programmer_t * pgm, AVRPART * p);
|
||||||
int (*chip_erase) (struct programmer_t * pgm, AVRPART * p);
|
int (*chip_erase) (struct programmer_t * pgm, AVRPART * p);
|
||||||
|
int (*unlock) (struct programmer_t * pgm, AVRPART * p);
|
||||||
int (*cmd) (struct programmer_t * pgm, const unsigned char *cmd,
|
int (*cmd) (struct programmer_t * pgm, const unsigned char *cmd,
|
||||||
unsigned char *res);
|
unsigned char *res);
|
||||||
int (*cmd_tpi) (struct programmer_t * pgm, const unsigned char *cmd,
|
int (*cmd_tpi) (struct programmer_t * pgm, const unsigned char *cmd,
|
||||||
|
@ -764,6 +765,8 @@ int avr_mem_hiaddr(AVRMEM * mem);
|
||||||
|
|
||||||
int avr_chip_erase(PROGRAMMER * pgm, AVRPART * p);
|
int avr_chip_erase(PROGRAMMER * pgm, AVRPART * p);
|
||||||
|
|
||||||
|
int avr_unlock(PROGRAMMER * pgm, AVRPART * p);
|
||||||
|
|
||||||
void report_progress (int completed, int total, char *hdr);
|
void report_progress (int completed, int total, char *hdr);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
2
main.c
2
main.c
|
@ -1077,7 +1077,7 @@ int main(int argc, char * argv [])
|
||||||
if (quell_progress < 2) {
|
if (quell_progress < 2) {
|
||||||
avrdude_message(MSG_INFO, "%s: erasing chip\n", progname);
|
avrdude_message(MSG_INFO, "%s: erasing chip\n", progname);
|
||||||
}
|
}
|
||||||
exitrc = avr_chip_erase(pgm, p);
|
exitrc = avr_unlock(pgm, p);
|
||||||
if(exitrc) goto main_exit;
|
if(exitrc) goto main_exit;
|
||||||
goto sig_again;
|
goto sig_again;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue