NFLic

STM8uLoader

Методы сортировки содержимого по блокам / словам / байтам (Статья 11)

В предыдущих статьях в класс Dumps.sc были добавлены модули записи в EEPROM память данных и FLASH памяти программ микоконтроллера STM8S103F3. Выяснилось, что для полноценного написания модулей/дампов, методов записи, стирания, копирования в EEPROM и FLASH память необходимо соответствующим образом отсортировать содержимое файла прошивки. Рассмотрим три варианта сортировки содержимого файла прошивки :

1 - Дополняем содержимое всей памяти где есть разрывы адресов например нулями $00 (а для FLASH памяти еще и кодом копировщика boot_FLASH) и записываем все блоки. При этом будут записываться ячейки всего адресного пространства памяти EEPROM и FLASH, а не только содержимое файла прошивки. В зависимости от размера файла прошивки может увеличится время записи, уменьшается ресурс циклов записи ячеек не предназначенных для записи. Необходимо перезаписывать ячейки с кодом начального копировщика boot_FLASH и при возможном сбое придется обращаться к программатору. Алгоритм заполнения недостающих ячеек менее сложный, чем сортировка по блокам/словам/байтам. Виду малого объема (630 байт) и большего ресурса записи ячеек, этот вариант можно было бы применить для EEPROM памяти на первом этапе.

2 - Дополняем содержимое блоков где есть разрывы адресов например нулями $00 (а для FLASH памяти еще и кодом копировщика boot_FLASH) и записываем только блоки полностью или частично присутствующие в файле прошивки. При этом не будут записываться ячейки блоков отсутствующих в файле прошивки, а не только содержимое файла прошивки. Время записи будет адекватно размеру файла прошивки, уменьшается ресурс циклов записи ячеек не предназначенных для записи только блоков, присутствующих в файле прошивке. Необходимо перезаписывать ячейки с кодом начального копировщика boot_FLASH и при возможном сбое придется обращаться к программатору. Алгоритм заполнения недостающих ячеек менее сложный, чем сортировка по блокам/словам/байтам. Если нет времени на реализацию сложного алгоритма и плата доступна для возможного подключения к программатору то этот вариант для FLASH памяти на первод этапе предпочтителен. Виду малого объема (630 байт) и большего ресурса записи ячеек, этот вариант можно было бы применить для EEPROM памяти как основной.

3 - сортируем содержимое по блокам/словам/байтам, разрывы адресов не дополняем, записывем только содержимое файла прошивки. При этом не будут записываться ячейки, отсутствующие в файле прошивки. Время записи может быть ниже чем в предыдущем пункте, не уменьшается ресурс циклов записи ячеек не предназначенных для записи. Нет необходимости в перезаписи ячеек с кодом начального копировщика boot_FLASH. Алгоритм сортировки по блокам/словам/байтам боле сложный чем алгоритм дополнения блоков недостающими байтами. Если плата не имеет доступа к подключению программатора (залита компаундом, заняты соответствующие пины, ...) то этот вариант предпочтителен. Однако, поскольку конвеер у семейства STM8 загружается из памяти FLASH 4-х байтовыми словами с начальным адресом выровненным по маске $xxx0, $xxx4, $xxx8, $xxxC, есть подозрение, что при побайтовой записи все равно переписывается все слово. По-этому, возможно, имеет смысл только сортировка по блокам/словам с дополнением недостающих адресов в словах напр. нулями, что еще более усложняет алгоритм сортировки. Также при определенном количестве последовательно расположенных слов в блоке имеет смысл запонить блок в незанятых адресах нулевыми ($00000000) словами, что может значительно сократить время записи. Конечно необходимо также учитывать временные затраты на прием микроконтроллером по UART информации с учетом выбранной скорости 128000, которая не может быть реализована на время записи, так как выполнение команд останавливается и в STM8S103F3 отсутсвует модуль прямого доступа к памяти. Этот вариант обязателен при прошивке в два и более этапа, когда в микроконтроллере не должны быть перезаписаны некоторые ранее записанные области.

 

В этой статье примем вариант по п.2 для записи памяти EEPROM и FLASH блоками. В одной из будущих статей можно будет задуматься о реализации сортировки по п.3 при записи в память FLASH. Начнем с EEPROM. Наполним файл EEPROM.asm мусором:

    stm8/
          TITLE “EEPROM.asm”
          MOTOROLA
          WORDS
          
          segment byte at 4000 - 403F 'EEPROM_1'
    ;      dc.w $1010, $1011, ... $101D, $101E       ; insert 15 words    
    val  CEQU    $1010
          REPEAT  
          DC.W    val
    val   CEQU    {val+1}
          UNTIL   {val eq $101F}    

          segment byte at 4243 - 427F 'EEPROM_3'
    ;      dc.b $00, $02, $04, ... $72, $74          ; insert 59 bytes 
    val   CEQU   0
          REPEAT
          DC.B    {2 mult val}
    val   CEQU    {val+1}
          UNTIL   {val eq 59}    

          segment byte at 4210 - 423F 'EEPROM_4'
          SKIP     13, $17                           ; insert 13 bytes $17

          segment byte at 4105 - 41FF 'EEPROM_4'
          SKIP     215, $F1                          ; insert 215 bytes $F1

          segment byte at 4040 - 40FF 'EEPROM_2'
    ;      dc.w $0000, $0000, ... $0000, $0000       ; insert 17 words $0000
    val   CEQU   0
          REPEAT
          DC.W    $0000
    ;val   CEQU    {val+1}
    ;      UNTIL   {val eq 17} 
    val   CEQU    {val+16}
          UNTIL   {val eq 272}    

          end
    ; после end должна быть хотя бы одна строка

 

Получим файл прошивки EEPROM.hex:

    :020000040000FA
    :104000001010101110121013101410151016101794
    :0E40100010181019101A101B101C101D101E75
    :1042430000020406080A0C0E10121416181A1C1E7B
    :1042530020222426282A2C2E30323436383A3C3E6B
    :1042630040424446484A4C4E50525456585A5C5E5B
    :0B42730060626466686A6C6E707274B2
    :0D4210001717171717171717171717171776
    :10410500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F19A
    :10411500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F18A
    :10412500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F17A
    :10413500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F16A
    :10414500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F15A
    :10415500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F14A
    :10416500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F13A
    :10417500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F12A
    :10418500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F11A
    :10419500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F10A
    :1041A500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1FA
    :1041B500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1EA
    :1041C500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1DA
    :0741D500F1F1F1F1F1F1F14C
    :104040000000000000000000000000000000000070
    :104050000000000000000000000000000000000060
    :0240600000005E
    :00000001FF

 

Метод заполнения блоков EEPROM :

	static SortedDictionary EEPROM_AdrBlcsSorting(SortedDictionary sort) {
		SortedDictionary sortAdrBt = new SortedDictionary(sort);			// для счетчика foreach
		SortedDictionary fullAdrBt = new SortedDictionary(sortAdrBt);		// для заполнения
		SortedDictionary EEPROM_AdrBlcks = new SortedDictionary();	// для возврата методом
		//EEPROM_AdrBlcks.Clear();
		int crntADR = 0;		// текущий адрес
	    int baseADR = 0;		// базовый адрес блока или смежныхблоков
	    Queue queue4000 = new Queue();
	    
		foreach(KeyValuePair kvp in sortAdrBt){
			// sortAdrBt содержит сортированные пары (адрес, байт) из файла прошивки,
			// адреса расположены в порядке возрастания, но не гарантируется непрерывность последовательности адреса
			// последовательность адресов может быть не выровнена по начальному и/или конечному адресу блока
			if ( (kvp.Key % 64) != 0 ){	//
				if(fullAdrBt.ContainsKey(kvp.Key - 1) == false){ // если предыдущий адрес отсутствует
					crntADR = kvp.Key - (kvp.Key % 64);
					for(;crntADR < kvp.Key; crntADR++){
						if(fullAdrBt.ContainsKey(crntADR) == false)
						fullAdrBt.Add(crntADR, 0x00); // 
						//else Console.WriteLine(" 1 Повторная запись в адрес 0x{0:X4}", crntADR);	
				   }
				}
			}	
			if ( (kvp.Key % 64) != 63){	//
				if(fullAdrBt.ContainsKey(kvp.Key + 1) == false){ // если следующий адрес отсутствует
					crntADR = kvp.Key;
					for(; (crntADR % 64) <= 62; ){
						crntADR++;
						if(fullAdrBt.ContainsKey(crntADR) == false)
						fullAdrBt.Add(crntADR, 0x00); // 
						//else Console.WriteLine(" 2 Повторная запись в адрес 0x{0:X4}", crntADR);
					}
				}	
			}
		}
		// fullAdrBt содержит сортированные пары (адрес, байт) из файла прошивки, 
	    // дополненные нулями до полного размера каждого используемого блока
	    // могут использоваться все или не все блоки
	    // используемые блоки могут граничить друг с другом
	    // или разделены неиспользуемыми блоками
	    // в следующем цикле в EEPROM_AdrBlcks должны быть сформированы пары <адрес, массив>
	    // где массив может содержать один или более смежных блоков
	    crntADR = 0;
	    baseADR = 0;
	    queue4000.Clear();
		foreach(KeyValuePair kvp in fullAdrBt){
			if(crntADR == 0) baseADR = crntADR = kvp.Key;
			if(fullAdrBt.ContainsKey(kvp.Key + 1)) 
				{	queue4000.Enqueue(kvp.Value);
					crntADR = kvp.Key + 1; }
			if(fullAdrBt.ContainsKey(kvp.Key + 1) == false){
				queue4000.Enqueue(kvp.Value);
				EEPROM_AdrBlcks.Add(baseADR, queue4000.ToArray());
				queue4000.Clear();
				crntADR = 0;
			}	
		}
		return EEPROM_AdrBlcks;
	}//SortedDictionary EEPROM_AdrBlcsSorting(SortedDictionary sort)

 

До сортировки блоков EEPROM :

 

 

Результат сортировки блоков EEPROM :

 

 

.

Метод заполнения блоков FLASH аналогичен предыдущему при следующих дополнениях :

- необходимо проверить отсутствие в файле прошивки адресов $8000...$8003, $9FF0...$9FFD ;

- необходимо заполнить адреса $8000...$8003, $9FF0...$9FFD кодом начального копировщика boot_FLASH .

Наполним файл FLASH.asm разрозненным содержимым:

    stm8/
          TITLE “FLASH.asm”
          MOTOROLA
          WORDS
          
          segment byte at 8004 - 803F 'start'
    ;      dc.w $1010, $1011, ... $101D, $101E       ; insert 15 words    
    val  CEQU    $1010
          REPEAT  
          DC.W    val
    val   CEQU    {val+1}
          UNTIL   {val eq $101F}    
    
          segment byte at 8243 - 827F 'FLASH_3'
    ;      dc.b $02, $04, $06, ... $7C, $7E          ; insert 64 bytes 
    val   CEQU   1
          REPEAT
          DC.B    {2 mult val}
    val   CEQU    {val+1}
          UNTIL   {val eq 59}    
    
          segment byte at 8210 - 822F 'FLASH_5'
          SKIP     13, $17    ; insert $17 bytes
    
          segment byte at 9241 - 9242 'FLASH_6'
    	  dc.b    $77
    	  
          segment byte at 8105 - 81FF 'FLASH_4'
          SKIP     215, $F1    ; insert $F1 bytes
    
          segment byte at 8040 - 80FF 'FLASH_2'
    ;      dc.w $0101, $0101, ... $0101, $0101       ; insert 17 words
    val   CEQU   0
          REPEAT
          DC.W    $0101
    ;val   CEQU    {val+1}
    ;      UNTIL   {val eq 17} 
    val   CEQU    {val+16}
          UNTIL   {val eq 272}    
    
          segment byte at 9FFE 'address'
    	  dc.w    $8004
    	  
          end
    ; после end должна быть хотя бы одна строка

 

Соответствующий файл прошивки FLASH.hex:

    :020000040000FA
    :108004001010101110121013101410151016101750
    :0E80140010181019101A101B101C101D101E31
    :10824300020406080A0C0E10121416181A1C1E201B
    :10825300222426282A2C2E30323436383A3C3E400B
    :10826300424446484A4C4E50525456585A5C5E60FB
    :0A827300626466686A6C6E707274D3
    :0D8210001717171717171717171717171736
    :0192410077B5
    :10810500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F15A
    :10811500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F14A
    :10812500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F13A
    :10813500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F12A
    :10814500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F11A
    :10815500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F10A
    :10816500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1FA
    :10817500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1EA
    :10818500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1DA
    :10819500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1CA
    :1081A500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1BA
    :1081B500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1AA
    :1081C500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F19A
    :0781D500F1F1F1F1F1F1F10C
    :108040000101010101010101010101010101010120
    :108050000101010101010101010101010101010110
    :0280600001011C
    :029FFE008004DD
    :00000001FF

 

Метод заполнения блоков FLASH :

	static SortedDictionary FLASH_AdrBlcsSorting(SortedDictionary sort) {
		SortedDictionary sortAdrBt = new SortedDictionary(sort);			// для счетчика foreach
		SortedDictionary FLASH_AdrBlcks = new SortedDictionary();	// для возврата методом
		int crntADR = 0;		// текущий адрес
	    int baseADR = 0;		// базовый адрес блока или смежных блоков
	    Queue queue8000 = new Queue();
	    
	    err_Msg = " OK";
	    if(	sortAdrBt.ContainsKey(0x8000) | sortAdrBt.ContainsKey(0x8001) | sortAdrBt.ContainsKey(0x8001) |
	      	sortAdrBt.ContainsKey(0x8003) | sortAdrBt.ContainsKey(0x9FF0) | sortAdrBt.ContainsKey(0x9FF0) |
	      	sortAdrBt.ContainsKey(0x9FF1) | sortAdrBt.ContainsKey(0x9FF2) | sortAdrBt.ContainsKey(0x9FF3) |
	      	sortAdrBt.ContainsKey(0x9FF4) | sortAdrBt.ContainsKey(0x9FF5) | sortAdrBt.ContainsKey(0x9FF6) |
	      	sortAdrBt.ContainsKey(0x9FF7) | sortAdrBt.ContainsKey(0x9FF8) | sortAdrBt.ContainsKey(0x9FF9) |
	      	sortAdrBt.ContainsKey(0x9FFA) | sortAdrBt.ContainsKey(0x9FFB) | sortAdrBt.ContainsKey(0x9FFC) |
	      	sortAdrBt.ContainsKey(0x9FFD) ) {err_Msg = " Имеются адреса кода копировщика"; return FLASH_AdrBlcks;}
	    crntADR = 0x8000;// дополняем кодом начального копировщика boot_FLASH
	    for(int i=0; i < boot_FLASH_8000.Length; i++, crntADR++){
	    	sortAdrBt.Add(crntADR, boot_FLASH_8000[i]);
	    }
	    crntADR = 0x9FF0;// дополняем кодом начального копировщика boot_FLASH
	    for(int i=0; i < boot_FLASH_9FF0.Length; i++, crntADR++){
	    	sortAdrBt.Add(crntADR, boot_FLASH_9FF0[i]);
	    }
	    SortedDictionary fullAdrBt = new SortedDictionary(sortAdrBt);		// для заполнения
	    
		foreach(KeyValuePair kvp in sortAdrBt){
			// sortAdrBt содержит сортированные пары (адрес, байт) из файла прошивки,
			// адреса расположены в порядке возрастания, но не гарантируется непрерывность последовательности адресов
			// последовательность адресов может быть не выровнена по начальному и/или конечному адресу блока
			if ( (kvp.Key % 64) != 0 ){	//
				if(fullAdrBt.ContainsKey(kvp.Key - 1) == false){ // если предыдущий адрес отсутствует
					crntADR = kvp.Key - (kvp.Key % 64);
					for(;crntADR < kvp.Key; crntADR++){
						if(fullAdrBt.ContainsKey(crntADR) == false)
						fullAdrBt.Add(crntADR, 0x00); // 
						//else Console.WriteLine(" 1 Повторная запись в адрес 0x{0:X4}", crntADR);	
				   }
				}
			}	
			if ( (kvp.Key % 64) != 63){	//
				if(fullAdrBt.ContainsKey(kvp.Key + 1) == false){ // если следующий адрес отсутствует
					crntADR = kvp.Key;
					for(; (crntADR % 64) <= 62; ){
						crntADR++;
						if(fullAdrBt.ContainsKey(crntADR) == false)
						fullAdrBt.Add(crntADR, 0x00); // 
						//else Console.WriteLine(" 2 Повторная запись в адрес 0x{0:X4}", crntADR);
					}
				}	
			}
		}
		// fullAdrBt содержит сортированные пары (адрес, байт) из файла прошивки, 
	    // дополненные нулями до полного размера каждого используемого блока
	    // могут использоваться все или не все блоки
	    // используемые блоки могут граничить друг с другом
	    // или разделены неиспользуемыми блоками
	    // в следующем цикле во FLASH_AdrBlcks должны быть сформированы пары <адрес, массив>
	    // где массив может содержать один или более смежных блоков
	    crntADR = 0;
	    baseADR = 0;
	    queue8000.Clear();
		foreach(KeyValuePair kvp in fullAdrBt){
			if(crntADR == 0) baseADR = crntADR = kvp.Key;
			if(fullAdrBt.ContainsKey(kvp.Key + 1)) 
				{	queue8000.Enqueue(kvp.Value);
					crntADR = kvp.Key + 1; }
			if(fullAdrBt.ContainsKey(kvp.Key + 1) == false){
				queue8000.Enqueue(kvp.Value);
				FLASH_AdrBlcks.Add(baseADR, queue8000.ToArray());
				queue8000.Clear();
				crntADR = 0;
			}	
		}
		return FLASH_AdrBlcks;
	}//SortedDictionary FLASH_AdrBlcsSorting(SortedDictionary sort)

 

До сортировки блоков FLASH :

 

 

Результат сортировки блоков FLASH :

 

 

Исходники к статье:[boot_PC.zip] , [EEPROM.asm] , [FLASH.asm] .

 

В следующей статье создаем реальный файл прошивки с областями RAM, EEPROM, FLASH. Прошиваем в микроконтроллер и передаем управление прикладной программе по адресу в командной строке.

 

[Класс Dumps. Модули записи в память FLASH (Статья 10)] [Оглавление.] [Изучаем область HW Registers (Статья 12)]