2020-10-05 06:17 AM
Hello,
I am working on an offline SWD programmer for a test jig, it is based on the widely available DAP open source implementation for ARM and it works fine so far however there is no command for dealing with option bytes and specicially with RDP, in my case the vast majority of time i only need to set RDP to level 1 after flashing but in some occurences i need to disable RDP (set to level 0) in order to reflash the target.And that is where is have an issue.
To clarify the setup :
The programmer is based on an STM32F411CEU6 which is the main MCU of the test jig control board, it also perform programming of other chips as well as some unitory testing (test pads and RF front end). The SWD programming of the target is done via SWD, i added a line for the STM32F411 reset just in case. The firmware file is in an SDHC card (SDIO 4bit).
The target is also an STM32F411CEU6, it is programmed with a firmware or ±360KB, verified and the RDP should be set to level 1.
In some case the device has been previously programmed thus it is necessary to reset RDP to level 0. IT is not clear to me wether reseting RDP to 0 is done by the erase all command, orif erase all is automatically performed whenwe set RDP to level 0, or if I need to set RDP to level 0 and then erase all.
Anyway so far the flash programming and verification is working, as well as erasing the pages that will be used, but i cannot get anything to work for RDP, as soon as i set the STM32 to RDP level 1 (externally, with an STlink) my DAP firmware cannot get the option bytes, nor perform mass erase, it always gets SWD errors.
I presume the reason is because i perform these operations at the wrong time, or something is not set.
Here is basically the workflow for flashing with RDP level 0:
//...dap internal setup, not SWD commands sent here...
dap_reset_link();
dap_swj_clock(50);
dap_target_prepare();
select_stm32(&target_id));
programPrepare(FLASH_START_ADDR, bin_size); // clear used pages
programFile(FLASH_START_ADDR, &fw); // program pages
deselect();
dap_disconnect();
Here is the details of dap_reset_link, dap_swj_clock and dap_target_prepare, which are part of the standard DAP implementation, untouched.Beloware select_stm32, and erasse_flash_mass which have been added. erase_flash_mass is taken from open source STLink sources, adjusting functions names to those of DAP.
I tried by invoquing erase_flash_mass at various points, before and after dap_target_prepare,
but also just after core hal in select_stm32, and after select_stm32, in all cases i got error responsesfromSWDcommands, and nomass erase is done.
bool dap_reset_link(void) {
uint8_t buf[128];
//-------------
buf[0] = ID_DAP_SWJ_SEQUENCE;
buf[1] = (7 + 2 + 7 + 1) * 8;
buf[2] = 0xff;
buf[3] = 0xff;
buf[4] = 0xff;
buf[5] = 0xff;
buf[6] = 0xff;
buf[7] = 0xff;
buf[8] = 0xff;
buf[9] = 0x9e;
buf[10] = 0xe7;
buf[11] = 0xff;
buf[12] = 0xff;
buf[13] = 0xff;
buf[14] = 0xff;
buf[15] = 0xff;
buf[16] = 0xff;
buf[17] = 0xff;
buf[18] = 0x00;
dbg_dap_cmd(buf, sizeof(buf), 19);
check(DAP_OK == buf[0], (char *)"SWJ_SEQUENCE failed");
//-------------
buf[0] = ID_DAP_TRANSFER;
buf[1] = 0; // DAP index
buf[2] = 1; // Request size
buf[3] = SWD_DP_R_IDCODE | DAP_TRANSFER_RnW;
dbg_dap_cmd(buf, sizeof(buf), 4);
return true;
}
bool dap_swj_clock(uint32_t clock) {
uint8_t buf[5];
buf[0] = ID_DAP_SWJ_CLOCK;
buf[1] = clock & 0xff;
buf[2] = (clock >> 8) & 0xff;
buf[3] = (clock >> 16) & 0xff;
buf[4] = (clock >> 24) & 0xff;
if (!dbg_dap_cmd(buf, sizeof(buf), 5)) {
return false;
}
if (DAP_OK != buf[0]) {
strcpy(error_message,"SWJ_CLOCK failed");
return false;
}
return true;
}
bool dap_target_prepare(void) {
dap_write_reg(SWD_DP_W_ABORT, 0x00000016);
dap_write_reg(SWD_DP_W_SELECT, 0x00000000);
dap_write_reg(SWD_DP_W_CTRL_STAT, 0x50000f00);
dap_write_reg(SWD_AP_CSW, 0x23000052);
return true;
}
bool select_stm32(uint32_t *found_id) {
uint32_t mcuid;
// Stop the core
dap_write_word(DHCSR, 0xa05f0003);
dap_write_word(DEMCR, 0x00000001);
dap_write_word(AIRCR, 0x05fa0004);
target_device.flash_size = (dap_read_word(STM32_FLASHSIZE) >> 16) * 1024;
*found_id = mcuid = (dap_read_word(DAP_DBGMCU_IDCODE) & 0xFFFUL);
for (int i = 0; i < STM32_DEVICES_COUNT; i++) {
if (mcuid == _stm32_devices[i].mcuid) {
target_device.name = _stm32_devices[i].name;
break;
}
}
if (target_device.name == NULL) return false;
return true;
}
int erase_flash_mass(void) {
/* wait for ongoing op to finish */
while (flash_busy()) yield();//wait_flash_busy(sl);
/* unlock if locked */
flash_unlock();
/* set the mass erase bit */
set_flash_cr_mer(1);
/* start erase operation, reset by hw with bsy bit */
set_flash_cr_strt();
/* wait for completion */
while (flash_busy());//wait_flash_busy_progress(sl);
//check_flash_error(sl); // already done by dap_write_word
/* relock the flash */
flash_lock(); //lock_flash(sl);
/* reset the mass erase bit */
set_flash_cr_mer(0);
/* todo: verify the erased memory */
return 0;
}
Also something odd, for reading optionbytes i use :
static int read_option_bytes_f4(uint32_t* option_byte) {
option_byte = dap_read_word(FLASH_F4_OPTCR);
return (uint32_t)option_byte;
}
and i call it after stm32_select, but the return value for first call and subsequent calls is always different, by this i mean that i get one value for the first call (always the same) and a different value for all other calls (always the same as well). Also sometimes i get 0x00000000.
Which mean something is wrong with the way i call this.
By first call and subsequent call i mean that the whole sequence SWD connection + option ytes reading (i tied this to a tact switch, so each time i press)
the return value with RDP level 1 is as follow :
// RDP level1 : Option bytes 0x08009035 first call
// RDP level1 : Option bytes 0x08008325 subsequent calls