Program SST Flash

Hi,

I am now working on embeded system project with external flash SST39VF1601. (CPU: lpc22xx). Since the OpenOCD now doesn’t support this type of flash, I am trying to make it. SST39VF1601, whose vendor is silicon storage technology, is CFI compatible, but uses some different commands. Now, I can read the CFI query table and erase the chip. I am not sure how to write the cfi_sst_write_block() function(I named it according to cfi_intel_write_block()).

I don’t know the functions of the following arrays. I think they are some codes, and can be created with GCC. I also refered to another pacth supporting atmel flash, and there are some differences between them. My question is which registers I shall use. Would someone explain more to me please?

I think they are copying(or writing) some data from one place to another, but I don’t know what are the source and destination.

Original version:

u32 word_32_code = {

0xe4904004, /* loop: ldr r4, [r0], #4 */

0xe5813000, /* str r3, [r1] */

0xe5814000, /* str r4, [r1] */

0xe5914000, /* busy ldr r4, [r1] */

0xe3140080, /* tst r4, #0x80 */

0x0afffffc, /* beq busy */

0xe314007f, /* tst r4, #0x7f */

0x1a000003, /* bne done */

0xe2522001, /* subs r2, r2, #1 */

0x0a000001, /* beq done */

0xe2811004, /* add r1, r1 #4 */

0xeafffff3, /* b loop */

0xeafffffe, /* done: b -2 */

};

u32 word_16_code = {

0xe0d040b2, /* loop: ldrh r4, [r0], #2 */

0xe1c130b0, /* strh r3, [r1] */

0xe1c140b0, /* strh r4, [r1] */

0xe1d140b0, /* busy ldrh r4, [r1] */

0xe3140080, /* tst r4, #0x80 */

0x0afffffc, /* beq busy */

0xe314007f, /* tst r4, #0x7f */

0x1a000003, /* bne done */

0xe2522001, /* subs r2, r2, #1 */

0x0a000001, /* beq done */

0xe2811002, /* add r1, r1 #2 */

0xeafffff3, /* b loop */

0xeafffffe, /* done: b -2 */

};

u32 word_8_code = {

0xe4d04001, /* loop: ldrb r4, [r0], #1 */

0xe5c13000, /* strb r3, [r1] */

0xe5c14000, /* strb r4, [r1] */

0xe5d14000, /* busy ldrb r4, [r1] */

0xe3140080, /* tst r4, #0x80 */

0x0afffffc, /* beq busy */

0xe314007f, /* tst r4, #0x7f */

0x1a000003, /* bne done */

0xe2522001, /* subs r2, r2, #1 */

0x0a000001, /* beq done */

0xe2811001, /* add r1, r1 #1 */

0xeafffff3, /* b loop */

0xeafffffe, /* done: b -2 */

};

Atmel Flash supported version:

  • u32 word_32_code = {

  • 0xe7085076, /* loop: str r5, [r7,r6] /* base+0x555=0xaa */

  • 0xe1aa5005, /* mov r5, r5, lsr #1 /* r5=0x55 */

  • 0xe18a6006, /* mov r6, r6, lsl #1 /* r6=0xaaa */

  • 0xe7085076, /* str r5, [r7,r6] /* base+0xaaa=0x55 */

  • 0xe18a5005, /* mov r5, r5, lsl #1 /* r5=0xaa */

  • 0xe1aa6006, /* mov r6, r6, lsr #1 /* r6=0x555 */

  • 0xe7083076, /* str r3, [r7,r6] /* base+0x555=0xa0 */

  • 0xe4094004, /* ldr r4, [r0], #4 */

  • 0xe5084010, /* str r4, [r1] */

  • 0xe5094010, /* busy ldr r4, [r1] */

  • 0xe3810040, /* tst r4, #0x80 */

  • 0x0afffffc, /* beq busy */

  • 0xe3210048, /* tst r4, #0x28 */

  • 0x1a000003, /* bne done */

  • 0xe2052021, /* subs r2, r2, #1 */

  • 0x0a000001, /* beq done */

  • 0xe2081014, /* add r1, r1 #4 */

  • 0xeaffffed, /* b loop */

  • 0xeafffffe, /* done: b -2 */

  • };

  • u32 word_16_code = {

  • 0xe18750b6, /* loop: strh r5, [r7,r6] /* base+0x555=0xaa */

  • 0xe1a050a5, /* mov r5, r5, lsr #1 /* r5=0x55 */

  • 0xe1a06086, /* mov r6, r6, lsl #1 /* r6=0xaaa */

  • 0xe18750b6, /* strh r5, [r7,r6] /* base+0xaaa=0x55 */

  • 0xe1a05085, /* mov r5, r5, lsl #1 /* r5=0xaa */

  • 0xe1a060a6, /* mov r6, r6, lsr #1 /* r6=0x555 */

  • 0xe18730b6, /* strh r3, [r7,r6] /* base+0x555=0xa0 */

  • 0xe0d040b2, /* ldrh r4, [r0], #2 */

  • 0xe1c140b0, /* strh r4, [r1] */

  • 0xe1d140b0, /* busy ldrh r4, [r1] */

  • 0xe3140080, /* tst r4, #0x80 */

  • 0x0afffffc, /* beq busy */

  • 0xe3140028, /* tst r4, #0x28 */

  • 0x1a000003, /* bne done */

  • 0xe2522001, /* subs r2, r2, #1 */

  • 0x0a000001, /* beq done */

  • 0xe2811002, /* add r1, r1 #2 */

  • 0xeaffffed, /* b loop */

  • 0xeafffffe, /* done: b -2 */

  • };

  • u32 word_8_code = {

  • 0xe7c75006, /* loop: strb r5, [r7,r6] /* base+0x555=0xaa */

  • 0xe1a050a5, /* mov r5, r5, lsr #1 /* r5=0x55 */

  • 0xe1a06086, /* mov r6, r6, lsl #1 /* r6=0xaaa */

  • 0xe7c75006, /* strb r5, [r7,r6] /* base+0xaaa=0x55 */

  • 0xe1a05085, /* mov r5, r5, lsl #1 /* r5=0xaa */

  • 0xe1a060a6, /* mov r6, r6, lsr #1 /* r6=0x555 */

  • 0xe7c73006, /* strb r3, [r7,r6] /* base+0x555=0xa0 */

  • 0xe4d04001, /* ldrb r4, [r0], #1 */

  • 0xe5c14000, /* strb r4, [r1] */

  • 0xe5d14000, /* busy ldrb r4, [r1] */

  • 0xe3140080, /* tst r4, #0x80 */

  • 0x0afffffc, /* beq busy */

  • 0xe3140028, /* tst r4, #0x28 */

  • 0x1a000003, /* bne done */

  • 0xe2522001, /* subs r2, r2, #1 */

  • 0x0a000001, /* beq done */

  • 0xe2811001, /* add r1, r1 #1 */

  • 0xeaffffed, /* b loop */

  • 0xeafffffe, /* done: b -2 */

  • };

Thanks and regards,

Kevin

It’s handwritten assembler. You’re free to use any registers you like, as the OpenOCD algorithm handling code takes care that all registers are saved and later restored.

The purpose of this code is to write a block of memory from RAM to flash. The source address is in r0, destination in r1, the number of items to be written is in r2, and the write command is in r3.

Regards,

Dominic

Hi, Dominic

Thank you for your quick reply. The command set used by SST is similar to that of Atmel. The word-program algorithm is 0x5555(0xaa)->0x2aaa(0x55)->0x5555(0xa0)->address(data). 0x5555(0xaa) means writing data 0xaa to address 0x5555. I think the codes maybe more or less the same as the one I mentioned above.

Though I am not very sure about how to write the codes yet, I will try it. If it passes testing, I will send you the source code. I am really very glad if you could give me more tutorial.

Thank you for your great work again!

Regards,

Kevin

Hi, Dominic

One more question. Here are some codes from the Atmel Flash supported version. I can get the functions of the codes thanks to the comments. The first line, I think, is to write data 0x55 to address base+0x555; my question is how can I load data 0x55 into r5.

Thanks a lot!

Kevin

0xe7085076, /* loop: str r5, [r7,r6] /* base+0x555=0xaa */

0xe1aa5005, /* mov r5, r5, lsr #1 /* r5=0x55 */

0xe18a6006, /* mov r6, r6, lsl #1 /* r6=0xaaa */

The code to support Intel compatible flashes I’ve written passes these values via the reg_params:

cfi_command(bank, 0x40, write_command);
buf_set_u32(reg_params[3].value, 0, 32, buf_get_u32(write_command, 0, 32));

You could also load the registers in the code, if the values are always going to be the same, but keep in mind that a flash bank might be made out of more than one device (e.g. 2x 16bit devices on a 32bit bus).

Regards,

Dominic

Hi, Dominic

Now there’s one problem about the software while programming SST flash SST39VF1601. That’s only half word is programmed for most of the data, with a few exceptions. Here shows more details.

Binary code:

E59FF018 E59FF018 E59FF018 E59FF018

E59FF018 B9205F80 E51FFFF0 E59FF018

80000040 800000E4 800000E8 800000EC

800000F0 00000000 800000F8 800000F4

Code read from flash using command mdw

fffff018 fffff018 fffff018 fffff018

fffff018 b9205f80 e51fffff e59fffff

ffff0040 800000e4 8000ffff 8000ffff

8000ffff 0000ffff 8000ffff 8000ffff

what’s the problem? So strange. There’s an exception on the second line. b9205f80.

Here’s the program function. cfi_sst_write_block().

int cfi_sst_write_block(struct flash_bank_s *bank, u8 *buffer, u32 address, u32 count)

{

cfi_flash_bank_t *cfi_info = bank->driver_priv;

target_t *target = cfi_info->target;

reg_param_t reg_params[9];

armv4_5_algorithm_t armv4_5_info;

working_area_t *source;

u32 buffer_size = 32768;

u8 write_command[8];

u32 target_result;

int i;

int retval;

u32 block_size = count;

u32 word_32_code = {

0xe7885006, /* loop: str r5, [r8, r6] */

0xe1a050a5, /* mov r5, r5, lsr #1 */

0xe7885007, /* str r5, [r8, r7] */

0xe1a05085, /* mov r5, r5, lsl #1 */

0xe7883006, /* str r3, [r8, r6] */

0xe4904004, /* ldr r4, [r0], #4 */

0xe5814000, /* str r4, [r1] */

0xe5914000, /* busy:ldr r4, [r1] */

0xe3140080, /* tst r4, #0x80 */

0x0afffffc, /* beq busy */

0xe2522001, /* subs r2, r2, #1 */

0x0a000001, /* beq done */

0xe2811004, /* add r1, r2, #4 */

0xeafffff1, /* b loop */

0xeafffffe, /* done:b -2 */

};

u32 word_16_code = {

0xe18850b6, /* loop: strh r5, [r8, r6] */

0xe1a050a5, /* mov r5, r5, lsr #1 */

0xe18850b7, /* strh r5, [r8, r7] */

0xe1a05085, /* mov r5, r5, lsl #1 */

0xe18830b6, /* strh r3, [r8, r6] */

0xe0d040b2, /* ldrh r4, [r0], #2 */

0xe1c140b0, /* strh r4, [r1] */

0xe1d140b0, /* busy:ldrh r4, [r1] */

0xe3140080, /* tst r4, #0x80 */

0x0afffffc, /* beq busy */

0xe2522001, /* subs r2, r2, #1 */

0x0a000001, /* beq done */

0xe2811002, /* add r1, r2, #2 */

0xeafffff1, /* b loop */

0xeafffffe, /* done:b -2 */

};

u32 word_8_code = {

0xe7c85006, /* loop: strb r5, [r8, r6] */

0xe1a050a5, /* mov r5, r5, lsr #1 */

0xe7c85007, /* strb r5, [r8, r7] */

0xe1a05085, /* mov r5, r5, lsl #1 */

0xe7c83006, /* strb r3, [r8, r6] */

0xe4d04001, /* ldrb r4, [r0], #1 */

0xe5c14000, /* strb r4, [r1] */

0xe5d14000, /* busy:ldrb r4, [r1] */

0xe3140080, /* tst r4, #0x80 */

0x0afffffc, /* beq busy */

0xe2522001, /* subs r2, r2, #1 */

0x0a000001, /* beq done */

0xe2811001, /* add r1, r2, #1 */

0xeafffff1, /* b loop */

0xeafffffe, /* done:b -2 */

};

armv4_5_info.common_magic = ARMV4_5_COMMON_MAGIC;

armv4_5_info.core_mode = ARMV4_5_MODE_SVC;

armv4_5_info.core_state = ARMV4_5_STATE_ARM;

/* flash write code */

if (!cfi_info->write_algorithm)

{

if (target_alloc_working_area(target, 4 * 15, &cfi_info->write_algorithm) != ERROR_OK)

{

WARNING(“no working area available, can’t do block memory writes”);

return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;

}

/* write algorithm code to working area */

if (bank->bus_width == 1)

{

target_write_buffer(target, cfi_info->write_algorithm->address, 15 * 4, (u8*)word_8_code);

}

else if (bank->bus_width == 2)

{

target_write_buffer(target, cfi_info->write_algorithm->address, 15 * 4, (u8*)word_16_code);

}

else if (bank->bus_width == 4)

{

target_write_buffer(target, cfi_info->write_algorithm->address, 15 * 4, (u8*)word_32_code);

}

else

{

return ERROR_FLASH_OPERATION_FAILED;

}

}

while (target_alloc_working_area(target, buffer_size, &source) != ERROR_OK)

{

buffer_size /= 2;

if (buffer_size <= 256)

{

/* if we already allocated the writing code, but failed to get a buffer, free the algorithm */

if (cfi_info->write_algorithm)

target_free_working_area(target, cfi_info->write_algorithm);

WARNING(“no large enough working area available, can’t do block memory writes”);

return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;

}

}

init_reg_param(&reg_params[0], “r0”, 32, PARAM_OUT);

init_reg_param(&reg_params[1], “r1”, 32, PARAM_OUT);

init_reg_param(&reg_params[2], “r2”, 32, PARAM_OUT);

init_reg_param(&reg_params[3], “r3”, 32, PARAM_OUT);

init_reg_param(&reg_params[4], “r4”, 32, PARAM_IN);

init_reg_param(&reg_params[5], “r5”, 32, PARAM_OUT);

init_reg_param(&reg_params[6], “r6”, 32, PARAM_OUT);

init_reg_param(&reg_params[7], “r7”, 32, PARAM_OUT);

init_reg_param(&reg_params[8], “r8”, 32, PARAM_OUT);

while (count > 0)

{

/* compute size of next block to write to FLASH */

u32 thisrun_count = (count > buffer_size) ? buffer_size : count;

/* copy data block to be written to FLASH to target RAM */

target_write_buffer(target, source->address, thisrun_count, buffer);

buf_set_u32(reg_params[0].value, 0, 32, source->address);

buf_set_u32(reg_params[1].value, 0, 32, address);

buf_set_u32(reg_params[2].value, 0, 32, thisrun_count / bank->bus_width);

cfi_command(bank, 0xa0, write_command);

buf_set_u32(reg_params[3].value, 0, 32, buf_get_u32(write_command, 0, 32));

cfi_command(bank, 0xaa, write_command);

buf_set_u32(reg_params[5].value, 0, 32, buf_get_u32(write_command, 0, 32));

buf_set_u32(reg_params[6].value, 0, 32, flash_address(bank, 0, 0x5555)-bank->base);

buf_set_u32(reg_params[7].value, 0, 32, flash_address(bank, 0, 0x2aaa)-bank->base);

buf_set_u32(reg_params[8].value, 0, 32, flash_address(bank, 0, 0));

/* run the programming code on the target */

if ((retval = target->type->run_algorithm(target, 0, NULL, 9, reg_params, cfi_info->write_algorithm->address, cfi_info->write_algorithm->address + (14 * 4), 10000, &armv4_5_info)) != ERROR_OK)

{

/* reset the FLASH device back to read mode */

cfi_sst_write_command_byte(bank, 0, 0xf0);

return ERROR_FLASH_OPERATION_FAILED;

}

/* get the programming result (in R4) */

target_result = buf_get_u32(reg_params[4].value, 0, 32);

if (target_result & 0x80 == 0)

{

/* reset the FLASH device back to read mode */

cfi_sst_write_command_byte(bank, 0, 0xf0);

ERROR(“target programming failure code R4=0x%08x”, target_result);

return ERROR_FLASH_OPERATION_FAILED;

}

buffer += thisrun_count;

address += thisrun_count;

count -= thisrun_count;

}

/* reset the FLASH device back to read mode */

cfi_sst_write_command_byte(bank, 0, 0xf0);

destroy_reg_param(&reg_params[0]);

destroy_reg_param(&reg_params[1]);

destroy_reg_param(&reg_params[2]);

destroy_reg_param(&reg_params[3]);

destroy_reg_param(&reg_params[4]);

destroy_reg_param(&reg_params[5]);

destroy_reg_param(&reg_params[6]);

destroy_reg_param(&reg_params[7]);

destroy_reg_param(&reg_params[8]);

return ERROR_OK;

}

Thank you and regards,

Kevin

Hi, Dominic

Now the OpenOCD is able to program SST39VF1601. I will send you a package later as soon as I finish other functions.

Regards,

Kevin