Lab. Microprocessadores

PCS2031 Curso Cooperativo prof. Jorge Kinoshita.

  1. quadrimestre 2016

Esse calendário contém as datas referentes das aulas para as turma de quinta-feira. no dia seguinte.

Aulas

turma1: Quinta 8:20-12:00H, turma2: Quinta 13:10-16:50H

Table of Contents

1 Aulas

2 2016-05-12 . Introducao a microprocessadores, com ênfase ao ARM.

2.1 Explicar o curso, sequencia das aulas e avaliacao.

2.2 Introducão ao ARM:

http://www.ee.ic.ac.uk/t.clarke/arch/lectures/Part1.ppt www.ee.ic.ac.uk/t.clarke/arch/lectures/Part2.ppt

Modifiquei para: http://www.pcs.usp.br/~jkinoshi/2012/Part1jk.ppt

2.3 referência: ARM Laboratory Exercises, cap. 1

Ler o primeiro capítulo. Fazer exercícios 1.7.1 e 1.7.2

3 2016-05-19 . Programming Basics (cap2) + Data Processing Operations (cap3).

3.1 PLANEJAMENTO:

Para essa aula não é necessário planejamento.

3.2 OBJETIVO:

  • capítulo 2 e parte do 3 da apostila, mas ao invés de usarmos o codewarrior para windows estaremos usando o gnuarm no linux ubuntu. cap2: todo o item 2.4: de 2.4.1 a 2.4.3

cap3: Faca de 3.10.1 ateh 3.10.4

3.3 instalar o gnuarm e ver o código em hello.s.

GCC-3.4 toolchain GNU/Linux (x86) http://www.gnuarm.com/bu-2.15_gcc-3.4.3-c-c++-java_nl-1.12.0_gi-6.1.tar.bz2 copiei em: http://www.pcs.usp.br/~jkinoshi/bu-2.15_gcc-3.4.3-c-c++-java_nl-1.12.0_gi-6.1.tar.bz2 Essa toolchain está compilada para 32 bits e foi a única toolchain que eu encontrei capaz de lidar bem com a placa evaluator7t do laboratório. Se voce quiser instalar em um linux de 64 bits terá que providenciar as bibliotecas necessárias para 32 bits.

bunzip2 bu-2.15_gcc-3.4.3-c-c++-java_nl-1.12.0_gi-6.1.tar.bz2
tar -xvf bu-2.15_gcc-3.4.3-c-c++-java_nl-1.12.0_gi-6.1.tar 

No diretorio Downloads/gnuarm-3.4.3/bin estao todos os executaveis como o compilador e o debuger.

Alterar o PATH .bashrc inserindo Downloads/gnuarm-3.4.3/bin para acessar os comandos arm-elf-* em outro diretorio. Exemplo:

-------------------------
export PATH=$PATH
PATH="$PATH:/home/manha/Downloads/gnuarm-3.4.3/bin"
-------------------------

Depois que alterar o PATH, abra uma nova aba do terminal ou um novo terminal para reexecutar o .bashrc e assim ter o PATH correto. Voce pode verificar o PATH atraves de: echo $PATH

Uma apostila que explica o ambiente GNU para a placa evaluator 7t está em: http://www.billgatliff.com/gettingStarted.html Nesse site stdio.h sumiu do hello.c:

#include <stdio.h>


int a_global = 10;

int foo ( int arg )
{
static int foo_counts = 0;

foo_counts++;
return foo_counts + arg;
}

int main ( void )
{
int nloops;
char* charstr = "hello, world!\n";

for( nloops = 0; nloops < 10; nloops++ ) {
printf( "%s", charstr );
foo( nloops );
}

return 0;
} 

Nessa apostila existe o código de hello.c; um programa que apenas imprime algumas strings. Vamos fazer uma simples experiencia de gerar um executavel, sem de fato se ter a pretensao de entender o codigo. O objetivo é apenas ter um primeiro contato com o gnuarm.

Edite o código de hello.c em home/manha (/home/tarde). Compile:

arm-elf-gcc -Wall -g -o hello hello.c

Entre no debuger e veja ele rodando de forma simulada:

arm-elf-gdb hello

(gdb) target sim
(gdb) load
(gdb) break main
(gdb) run
(gdb) continue

Uma forma mais interessante é ver através de:

arm-elf-gdb -tui hello

pois apresenta o debug junto com o código fonte em duas janelas. C-x 2 (control-x 2) habre outras janelas, para por exemplo se ver os registradores. Digite duas vezes C-x 2 para ver os registradores.

Uma outra forma de ver registradores é

(gdb) înfo registers
(gdb) x $pc
(gdb) x $r0
(gdb) x $cpsr

No curso, entretanto, vamos estudar o código assembly. O código assembly de hello.c é gerado através de:

arm-elf-gcc -S hello.c

gerando hello.s

O código hello.s pode gerar o hello através de:

arm-elf-gcc -Wall -g -o alo hello.s

e debugado normalente como:

arm-elf-gdb -tui alo

Para maiores informacoes sobre os comandos em si, indicamos referencias tais como: http://www.gnu.org/manual/manual.html http://www.microcross.com/gnu-arm7t-microcross.pdf http://bel.gsi.de/scripts/gnu-arm-assy-quick-ref.pdf

3.4 Usando o gnuarm, rode o programa da pagina 2-3 da apostila ARM Lab (item 2-2).

Observe que vc. deverá colocar o programa no formato GNU. Uma forma de fazer isso é observar o hello.s e fazer as modificacoes. Em caso de dúvidas sobre as diretivas, consulte os manuais em https://sourcery.mentor.com/sgpp/lite/arm/portal/release830; em particular consule o manual do GNU Assembler. A modificação já feita do codigo assembly do item 2-2 fica assim no gnumarm:

---------------------------------------------------

        .text
        .globl main
main:
        MOV     r0, #15                 
        MOV     r1, #20
        BL      firstfunc               
        MOV     r0, #0x18               
        LDR     r1, =0x20026            
        SWI     0x123456                
firstfunc:
        ADD     r0, r0, r1              
        MOV     pc, lr                  
--------------------------------------------------

Para isso vc. pode fazer:

gedit item-2-2.s e fazer o copy and paste.

3.4.1 Uma software interrupt

No código temos:

LDR     r1, =0x20026            
SWI     0x123456                

que se refere a uma software interrupt pedindo um servico do monitor da placa evaluator7t. Como estamos rodando de forma simulada, essa software interrupt irá travar o gdb. Assim, nao rodem o SWI (basta colocar um breakpoint). Uma pergunta interessante é:

  • qual a diferenca entre LDR e MOV? Observem que ambas as instrucoes carregaram valores imediatos nos registradores. A diferenca é que com LDR é possível carregar valores quaisquer de 32 bits enquanto que com o MOV não. Isso será importante para a questao 3.10.1

3.5 Fazer todos os exercícios do item 2.4 pagina 2-8 da apostila usando o gnuarm.

Observacao sobre os exercicios As perguntas do item 2.4 se referem ao codewarrior (ambiente usado pela apostila no lugar do gnuarm); por isso pensem nas questoes referindo-se ao gnuarm. 2.4.1 - no ambiente codewarrior existe um make. Durante o nosso curso, nao precisaremos do make uma vez que o proprio arm-elf-gcc dispara o assembler e o linker. Para ver o que foi gerado pelo arm-elf-gcc basta fazer:


$ ls -alt|more


Esse comando coloca no topo os arquivos recentemente modificados ou criados. Apenas relatem o que foi feito - uso do arm-elf-gcc, geracao do arquivo e arm-elf-gdb para rodá-lo. 2.4.2 - A pergunta 2.4.2 pergunta sobre a diferenca entre step e stepin no codewarrior . Para o gdb, a pergunta se refere a: step: passo a passo entrando na rotina next: passo a passo mas sem entrar na rotina. Existe um problema GRAVE no uso do next. O arm-elf-gdb misteriosamente se perde ao ver um label como:

  mov r0,1
label:
  move r0,2

Ao executar mov r0,1; o debugger nao pula para a instrucao seguinte usando next. Por isso, muito preferencialmente use 'step'.

2.4.3 - Se quisermos ver os registradores na tela do arm-elf-gdb usando C-x 2, teremos dois formatos hexa e decimal. Porém, é possível observar memória e registradores em outros formatos. Veja o manual do gdb - http://sourceware.org/gdb/download/onlinedocs/gdb/index.html Exemplos:

p/x $pc
p/x $cpsr
x/i $pc

Usando o help help x help p

Voce deve ter observado que x - serve para ver memoria externa.

x/d $r1

apresenta o conteudo de r1 em hexadecimal e o conteudo apontado por r1 na memoria em decimal.

p/d $r1

apresenta o conteudo de r1 em decimal.

Uma forma PERIGOSA de ver os bits do registrador de status eh:

p/t $cpsr

porem, os primeiros zeros serao OMITIDOS e voce pode estar vendo menos que 32 bits. Tome cuidado! Compare o cpsr com o comando "info registers". Melhor p/x $cpsr

3.6 Estude o capítulo 3 da apostila.

3.6.1 pg 3-7:

ADD r0, r1, #0xc5, ROR 10 ver desenho na pagina 3-6.

1100 0101 para a direita em um registrador de 32 bits

depois de rodar 8 vezes temos

1100 0101 0000 … e depois de 10 vezes temos:

0011 0001 0100 …

o que fornece um o resultado da apostila:

31 40 00 00 0011 0001 0100 0000 0000 0000 0000 0000

3.7 Faca os exercicios 3.10.1 a 3.10.4 da pagina 3-11, 3-12. Seguem comentarios sobre cada exercicio.

3.7.1 3.10.1 - Signed and unsigned addition

Use LDR para carregar valores de 32 bits em registradores. Ao inves de fazer:

mov r1,#0x12345678

faça:

ldr r1,=0x12345678

ADD nao atualiza as flags do CPSR ADDS atualiza as flags do CPSR.

Se representarmos numeros em 4 bits em complemento de 2, podemos representar desde o -8 ateh o 7. Ao somarmos -1 e 1 temos carry mas nao temos overflow. Somando 5+4, temos que 9>7 e portanto temos o overflow.

3.7.2 3.10.2 - Multiplicacao de numeros

  • no resultado tivemos a flag N setada, embora multiplicamos dois numeros negativos (e o resultado deveria ser positivo). Isso deve ter acontecido porque o bottom foi "negativo" e a flag foi atualizada depois do resultado.

O resultado faz sentido? Se quisermos observar o bottom (32 bits menos significativos) contendo o numero todo, temos que multiplicar numeros que caibam em representacoes de 16 bits. Para entender melhor veja no site da ARM, a especificacao da instrucao MUL. http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0068b/CIHIHGGJ.html

Obs: Na apostila ARM Lab Manual temos na pg 3-5 UMULL r6, r8, r0, r1 ; {r6,r8} = r0 × r1 onde aparentemente r6 eh o mais significativo. Pelo site da ARM temos que o r6 (primeiro argumento) eh o menos significativo.

3.8 Envie o codigo fonte e possiveis comentarios dos exercicios no final da aula via email, subject: labmicro cap 3A.

3.9 Desempenho da classe:

algumas equipes nao conseguiram terminar todos os exercicios.

4 2016-06-02 . Data Processing Operations (cap3).

4.1 PLANEJAMENTO:

4.1.1 A - leiam o capitulo 3 da apostila.

4.1.2 B - Respondam as seguintes perguntas numa folha a mão (o professor fará chamada oral antes da aula).

  • 1. O que há de errado nas seguintes instrucoes:
    a. ADD r3,r7, #1023
    b. SUB r11, r12, r3, LSL #32
    

    Sugestao:

    • vejam os codigos de maquinas e observem que certos numeros nao servem para fazer o codigo de maquina.
    • coloquem no gnuarm e vejam o erro.
  • 2. Sem usar a instrucao MUL, de as seguintes instrucoes para multiplicar o registrador R4 por:
    a. 132
    b. 255
    c. 18
    d. 16384
    

    Dicas (apenas para pensar em como resolver os itens a,b,d,e acima):

    • como vc. faria para multiplicar um valor por 4? Dica: use o MOV com o deslocamento. r1 = 4*r0
    • como vc. faria para multiplicar um valor por 5? Dica: use ADD com deslocamento - r1 = r0+4*r0
    • como vc. faria para multiplicar um valor por 3? r1 = r0*4 - r0; veja a diferenca entre SUB e RSB
    • como vc. faria para multiplicar um numero por 15? Multiplica por 3 e depois por 5.
  • 3. Escreve uma rotina que compara 2 valores de 64-bits usando somente 2 instrucoes. (dica: a segunda instrucao é condicionalmente executada, baseada no resultado da primeira comparacao).
  • 4. Escreva uma rotina que desloque um valor de 64-bits (armazenado em 2 registradores r0 e r1) de um bit para a direita.
  • 5. idem 4, para a esquerda.

4.1.3 C - prepare a solucao de 3.10.7

  • tragam numa folha (nao vale no computador), a solucao rascunhada a mão do exercicio de divisao 3.10.7; ou seja, como é o algoritmo da divisao. Nao eh para trazer todo o algoritmo em codigo ARM já implementado, mas se quiser coloque algumas partes em codigo ARM. A operacao de divisao deve ser feita com shift como faz a profa. do primário e nao o algoritmo ineficiente e simples que retira um numero do outro.

Veja: http://courses.cs.vt.edu/~cs1104/Division/ShiftSubtract/Shift.Subtract.html e coloque no papel a simulacao de 1101 dividido por 10.

4.2 OBJETIVO:

  • terminar capítulo 3 da apostila. Fazer os exercicios de 3.10.5 ateh 3.10.8

4.3 DICAS e Observacoes:

4.3.1 3.10.5 -

nao percam tempo com a restricao da apostila: usem instrucoes condicionais para facilitar. No cap. 5 da apostila existe uma tabela com todas as condicoes possiveis. Uma dica para se ter o absoluto de um numero eh fazer (zero - numero) caso o numero seja negativo.

4.3.2 3.10.7 Division

o arm-elf-gcc nao estah compilando uma instrucao que lide com RRX como mov … RRX porem compila outras como mov … ROR

4.3.3 3.10.8

  • Erro na apostila ARM Lab Manual: A sequencia b010 011 001 000 101 111 110 100 nao eh um codigo de gray de 3 bits (ex: erro ao passar de 000 para 101 alterando dois bits). O codigo pode ser

000 001 011 010 110 111 101 100. Se tiver duvidas em como se forma o codigo gray, consulte o wikipedia.

4.4 Envie o codigo fonte e possiveis comentarios dos exercicios no final da aula via email, subject: labmicro cap 3B.

4.5 Desempenho da classe:

metade das equipes nao conseguiu implementar o 3.10.8 (mas vale a pena implementar?). O 3.10.8 poderia ser visto como um exercicio extra ou ainda eliminado.

5 2016-06-09 . Loads and Stores (cap4)

5.1 PLANEJAMENTO:

5.1.1 A Leia o capitulo 4.

5.1.2 B Respondam as questoes em uma folha à mão:

B.1) Descreva o conteúdo do registrador R13 depois que as seguintes instruções forem executadas, assumindo que a memória contenha os valores mostrados abaixo. O registrador R0 contém 0x24, e o sistema de memória é little-endian (o menos significativo é colocado no endereco mais baixo).

EndereçoConteúdo
0x240x06
0x250xFC
0x260x03
0x270xFF
LDRSB r13, [r0]
LDRSH r13, [r0]
LDR r13,[r0]
LDRB r13,[r0]

B.2) Indique se as seguintes instruções usam o modo pré ou pós indexado de endereçamento:

STR r6, [r4,#4]
LDR r3, [r12], #6
LDRB r4, [r3,r2]!
LDRSH r12, [r6]

B.3) Calcule o endereço efetivo das seguintes instruções se o registrador r3 = 0x4000 e o registrador r4 = 0x20

STRB r9, [r3,r4]
LDRB r8,[r3,r4,LSL #3]
LDR r7, [r3], r4
STRB r6, [r3], r4, ASR #2

B.4) O que há de errado na seguinte instrução?

LDRSB r1,[r6],r3,LSL #4

5.1.3 C - rascunhe a solucao

Cada equipe deve trazer numa folha de papel a resposta em letra de mao (nada de coisa impressa) para a seguinte pergunta. Escreva o código em Assembly que faça:

for (i=0; i<8; i++) {
  a[i] = b[7-i];
}

Vai ter mais nota quem colocar o programa mais proximo da realidade; ou seja, evitem usar pseudo instrucoes. Para observar um codigo bem proximo da realidade, sugiro instalar o gnuarm na maquina de voces e testar. Procurem usar as seguintes instrucoes em seu código: LDR ou ADR (isto é: declarem os dados na memória e leiam de lá; por exemplo, onde comeca o a array b e a array a). BGE (usem instrucoes que facam o desvio condicional, nao necessariamente BGE). RSB (para o 7-i) STR (isto é: armazene de fato o dado na memória).

5.2 OBJETIVO:

Fazer os exercicios do capitulo 4.

5.3 Observacoes

5.3.1 Na apostila tem um erro no item:

4.3.1 Direct loading with MOV and MVN

MOV r0, #0x1, 30 ; r0 = 1020         32 - 30 = 2; 2 ** 2 = 4 portanto pula de 4 em 4.
MOV r0, #0xFF, 28 ; r0 = 4080        32 - 28 = 4; 2 ** 4 = 16 portanto pula de 16 em 16.
MOV r0, #0x1, 26         ; r0 = 4096   32 - 26 = 6; 2 ** 6 = 64 portanto pula de 64 em 64.

mas na realidade eh:

│0x8218 <main>           mov    r0, #4           1 *4 = 4                                 │
│0x821c <main+4>         mov    r0, #4080        255 * 16 = 4080                          │
│0x8220 <main+8>         mov    r0, #64 ; 0x40   1 * 64 = 64.           

5.3.2 Comentários sobre os exercícios:

  • 4.5.1
    Assignments with operands in memory Assume an array of 25 words. A compiler associates variables x and y with registers r0 and r1, respectively. Assume that the base address for the array is located in r2. Translate this C statement/assignment using the post-indexed form:
    x = array[5] + y
    

    Now try writing it using the pre-indexed form.

    Declare um programa em C bem simples que contenha uma array como variável global e que fique em um loop fazendo:

    x = array[5] + y
    

    Compile o programa com a opcao -S para gerar o codigo em assembly. Observe como o assembly gerou a área de dados e como o label array foi usado no programa com instrucoes do tipo ADR. Nao se preocupe em entender todo o codigo gerado. O objetivo eh apenas observar a "cara" do assembly no mundo GNU que é diferente do assembly usado na apostila - o codewarrior.

    Uma forma simples de se declarar dados, por exemplo, uma array, estah em http://www.coranac.com/tonc/text/asm.htm :

        mov     r2, #1
    @ Byte loads
        adr     r0, bytes
        ldrb    r3, bytes       @ r3= bytes[0];     // r3= 0x000000FF= 255
        ldrsb   r3, bytes       @ r3= (s8)bytes[0]; // r3= 0xFFFFFFFF= -1
        ldrb    r3, [r0], r2    @ r3= *r0_b++;      // r3= 255, r0++;
    @ Halfword loads
        adr     r0, hwords
        ldrh    r3, hwords+2    @ r3= words[1];     // r3= 0x0000FFFF= 65535
        ldrsh   r3, [r0, #2]    @ r3= (s16)r0_h[1]; // r3= 0xFFFFFFFF= -1
        ldrh    r3, [r0, r2, lsl #1]    @ r3= r0_h[1]? No! Illegal instruction :(
    
    @ Byte array: u8 bytes[3]= { 0xFF, 1, 2 };
    bytes:
        .byte   0xFF, 1, 2
    @ Halfword array u16 hwords[3]= { 0xF001, 0xFFFF, 0xF112 };
        .align  1    @ align to even bytes REQUIRED!!!
    hwords:
        .hword  0xF110, 0xFFFF, 0xF112
    

    Para observar os dados na memória dentro do gdb, voce pode fazer

    x/20 0x100
    

    para ver 20 words a partir de 0x100

    x/21h hwords // hwords eh o label no codigo acima
    

    para ver 21 half words a partir do label hwords.

    x/20db array // hwords eh o label no codigo acima
    

    para ver 20 bytes em formato decimal a partir do label array

    Terminando este item pule para o 4.5.2, lembrando que mais referencias estao em: http://www.coranac.com/tonc/text/asm.htm : pagina mostrando diversos codigos e dados para o gnu assembler

    http://www.microcross.com/gnu-arm7t-microcross.pdf http://bel.gsi.de/scripts/gnu-arm-assy-quick-ref.pdf

    e os manuais do GNU estão (por exemplo) em: https://sourcery.mentor.com/sgpp/lite/arm/portal/release830

  • 4.5.4
    Suponha que ao inves de b) initPointers (int *a, esteja escrito b) initPointers (int *array,
  • 4.5.5, 4.5.6 sao muito parecidos.
    A diferenca eh que 4.5.6 nao pede para calcular a sequencia na memoria, podendo simplesmente usar registradores para isso. Nao existe de fato muita diferenca. Alguns alunos questionaram se 4.5.6 deveria calcula f(n) para qualquer n inteiro. O primeiro problema eh que o resultado deveria caber na memória do computador e portanto n sempre terah que ser limitado: nao eh isso que vamos fazer. Assuma n limitado para resultados cabendo em byte ou word.

5.4 Envie o codigo fonte e possiveis comentarios dos exercicios no final da aula via email, subject: labmicro cap 4.

5.5 Desempenho da classe:

algumas turmas nao conseguiram o 4.5.6. Uma nao conseguiu o 4.5.5.

6 2016-06-16 . Conditional Executiong Loops (cap5).

6.1 PLANEJAMENTO

6.1.1 A. Leia o Capitulo 5

6.1.2 B. Responda em uma folha de papel aa mao para entregar ao professor no comeco da aula - serve de base para a chamada oral individual.

  • 1. Traduza as seguintes instrucoes em uma unica instrucao ARM:
    • a. adicione resgistradores r3 e r6 somente se N = 0 (N estah "clear"). Armazene o resultado no registrador r7.
    • b. adicione resgistradores r3 e r6 somente se N = 1. Armazene o resultado no registrador r7.
    • c. Multiplique os registradores r7 e r12, colocando os resulados no registrador r3 somente se C estah setado (C = 1) e Z = 0
    • d. Multiplique os registradores r7 e r12, colocando os resulados no registrador r3 somente se C clear and Z set .
    • e. Compare os registradores r6 e r8 somente se Z estah zerado.
    • f. Compare os registradores r6 e r8 somente se Z set, N ≠ V
      1. Observe a seguinte funcao em C:
      int foo(int x, int y) {
         if (x + y) >= 0)
               return 0;
         else
               return 1;
      }
      

      Suponha que ela tenha sido compilada e traduzida no seguinte codigo:

      foo        ADDS r0,r0,r1
                   BPL PosOrZ
      done      MOV r0, #0
                   MOV pc, lr
      PosOrZ  MOV r0,#1
                   B done
      

      O compilador gerou o código corretamente? O compilador retorna 0 ou 1 em r0. Se não está bom o código, corrija. Altere o código para que ele execute a funcao em somente 4 instrucoes (dica: use execucao condicional).

6.1.3 C. IMPORTANTE: Fazer o rascunho de 5.5.4 em folha de papel rascunhado a mão; por exemplo, faça o diagrama de estados.

5.5.4 Finite state machines: a nonresetting sequence recognizer

  • 1. Consider an FSM with one input X and one output Z. The FSM asserts its output Z when it recognizes an input bit sequence of b1011. The machine keeps checking for the sequence and does not reset when it recognizes the sequence. Here is an example input string X and its output Z:
    X = ...0010110110...
    Z = ...0000010010...
    

    Write ARM assembly to implement the sequence recognizer. Start with the initial input X in r1. Finish with the output Z in r2 at the end of the program.

  • 2. Now write the code to recognize any sequence Y up to 32 bits. Start with the recognizing sequence Y in r8 and the size of Y in r9. For example, to recognize the sequence Y = b0110110, then r8 = 0x36 and r9 = 0x7 before program execution. Everything else should be the same is in Step 1. Make sure that your program works for every case, including the case when r9 = 1 or r9 = 32.

    Uma idéia é fazer o item 2 e usá-lo para resolver o item 1.

6.2 Objetivo

Fazer os exercicios do capitulo 5 da apostila em 5.5

6.3 Observacoes

6.3.1 5.5.2

Se vc. considerar que nao eh necessario usar MOVNE, delete essa instrucao do codigo sugerido. (mas talvez precise sim - um registrador nao pode ser origem e destino na multiplicacao).

6.3.2 5.5.3

  • Find maximum value
    In this exercise, you are to find the largest integer in a series of 32-bit unsigned integers. The length of the series is determined by the value in register r5. The maximum value is stored in the memory location 0x5000 at the end of the routine. The data values begin at memory location 0x5006. Choose 11 or more integers to use. Use as much conditional execution as possible when writing the code. Demonstrate the program to your lab instructor and print out the memory space starting at 0x5000 before and after the program runs. Be sure to include enough memory space to show all of your 32-bit integer values.

    0x5006 não é múltiplo de 4 - as words devem estar alinhadas em múltiplos de 4. Sinta-se aa vontade para deixar o valor maximo em registrador em passar para a memoria.

    • Obs: A apostila foi escrita para o codewarrior e estamos usando o gnuarm e nesse ambiente é razoavelmente simples definir toda uma área de dados em uma certa posição de memória (no ldscript), mas não é simples definir que dados sejam alocados em um endereço específico - para isso podemos usar ponteiros para a posição fixa.
      Nesse laboratório, ao invés de usar 0x5000, defina uma área de dados de 100 bytes assim:
      dados .space 100
      

      e no programa podemos fazer, por exemplo:

      LDR r0,=dados+4
      

      e dessa forma não dependemos da posição fixa 0x5000 Caso queira dados jah pre-inicializados faca:

      dados .word 0x1, 0x2 ...
      

6.3.3 5.5.4

se quiser, declare um numero binario como: 0b10101 dentro do codigo.

6.4 Envie o codigo fonte e possiveis comentarios dos exercicios no final da aula via email, subject: labmicro cap 5.

7 2016-06-23 . Subroutines (cap6)

7.1 PLANEJAMENTO

7.1.1 A. Ler cap 6.

7.1.2 B. Responda (rascunho no papel)

1 O que há de errado com as seguintes instruções:

a) STMIA r5!, {r5, r4, r9}
b) LDMDA r2, {}
    STMDB r15!, [r0-r3, r4, lr}

2 Se o registrador r6 possui 0x8000 (como ponteiro para a memória); após executar

STMIA r6,{r7,r4,r0,lr}

o que fica em r0, r4, r7 e em lr?

3 Assuma que a memória e registradores estejam:

0x80100x1
0x800C0xfeeddeaf
0x80080x00008888
0x80040x12340000
0x80000xbabe0000

r0=0x13; r10xffffffff; r2 = 0xeeeeeeee; r3 0x8000

Descreva a memória e conteúdos dos registradores após a instrução:

LDMIA r3!, {r0,r1,r2}

4 Suponha que a pilha esteja como o diagrama abaixo. Que instrução seria necessária para sair do estado original e ir para o estado a), depois b) e depois c)?

EndereçoOriginalABC
0x80100x10x10x10x1
0x800C0xfeeddeaf0xfeeddeaf0xfeeddeaf0xfeeddeaf
0x80080xbabe22220xbabe22222
0x80040x12340000
0x8000

7.1.3 C. IMPORTANTE

Voce pode escolher entre:

  • apresentar o codigo rodando no seu PRÓPRIO computador no comeco de aula
  • ou rascunho no papel.

6.5.2 Bubble sorting 6.5.3 Magic Squares

7.2 OBJETIVO

Exercicios 6.5 do 6.5.1 ateh 6.5.4; o resto serah feito na proxima aula.

7.3 Observacoes

6.5.1 transmit the arguments by way of the stack with two subroutines, func1 and func2, that demonstrate stack functionality. É importante que o endereco de retorno seja colocado na pilha em func1.

6.5.2 Bubble Sort

  1. Modify your code to utilize a full descending stack. Sorting must be done on the stack only. Once the stack is sorted, store the sorted stack back to the original array of memory locations starting at 0x4001.

The algorithm for the bubble sort is as follows: a. Compare adjacent elements. If the first element is greater than the second, swap them.

b. Do this for each pair of adjacent elements, starting with the first two and ending with the last two. At this point the last element should be the greatest. c. Repeat the steps for all elements except the last one. d. Repeat this process for one fewer element each time, until you have no more pairs to compare.

Dado que eh muito confuso, nao estarei cobrando esse uso de pilha. Podem pular esse item 6.5.2.-2. Entretanto, seguem observacoes colhidas ao longo do curso. Porem, pulem esse item:

Está confuso como utilizar o full descending stack - uma forma de organizar usando pilha somente é torre de Hannoy mas usando o bubble sort estamos mexendo em elmentos de uma array. Uma idéia é: trabalha com duas estruturas - uma array onde se descobre o maior e a pilha que vai armazenando o maior elemento em cada iteracao. Observar que a array eh de bytes enquanto que a pilha eh de words (o mais simples eh desperdicar memoria ao usar os bytes como words). Outra idéia (grupo Joao) - usar duas pilhas. A pilha eh varrida a cada comparacao jogando o maior valor para a segunda pilha. Na primeira pilha sobre o menor valor. A segunda pilha com N-1 elementos é totalmente trasportada para a primeira pilha. O processo se repete para os N-1 elementos da primeira pilha.

6.5.3 Quadrado Magico Nao é necessário preocupar-se em colocar o quadrado mágico em 0x4000. É mais fácil declarar na memória ao final do programa algo como .word 1,4, … colocando as words do quadrado mágico.

6.5.4 More stacks Write ARM assembly to implement a push operation without the use of load/store multiple instructions. Write the code to handle bytes, half-words, and words. Use r0 to indicate the data type. A value of 1 in r0 indicates that a byte is to be pushed, 2 indicates a half-word, and 4 indicates a word. Put the data to push in r1.

Lembrar que sp eh sempre um multiplo de 4. Tem que tomar um certo cuidado ao empilhar byte ou half word para que o sp permaneca multiplo de 4; ou seja, dependendo do caso a memoria eh desperdicada. O mais facil eh sempre alocar 4 bytes mesmo que seja para um byte apenas.

Dica para olhar a pilha no gdb:

x/20 $sp

7.4 Envie o codigo fonte e possiveis comentarios dos exercicios no final da aula via email, subject: labmicro cap 6.

8 2016-06-30 . P1 - Subroutines (cap6)

8.1 PLANEJAMENTO

para a prova preparem um pendrive contendo tudo o que acharem necessario para desenvolverem programas para o ARM, desde programas de experiencias passadas, livro sobre o instruction set da ARM, manuais do gdb, etc.

8.2 Prova

A prova ocorrerá na sala de laboratorio de sistemas digitais. Venham com seus laptops preparados (toolchain ou simulador instalado, codigos fonte para consulta, manuais, etc.).

8.3 prova tarde

8.3.1 pares:

dado um numero, identificar se ele é primo.

8.3.2 impares:

colocar em um vetor, na memoria, os primeiros 30 numeros primos.

8.3.3 tempo de prova

2 horas.

9 2016-07-07 . Memory Mapped Peripherals (cap7).

9.1 PLANEJAMENTO:

9.1.1 A. Leitura, alem do capt 7 da aposlita, consulte as seguintes referencias para fazer o planejamento:

9.1.2 B. Rascunho de codigo

como planejamento rascunhem a solucao de cada um dos exercicios abaixo trazendo isso em PAPEL (retirarei pontos caso nao seja em papel rascunhado - nao deve ser impresso).

Os exercicios abaixo devem ser feitos com um cuidado extra mostrando como o codigo assembly faz de fato a entrada e saida, passo a passo. Nao existe informacao suficiente na apostila e por isso estou passando em anexo o manual da placa dui…pdf. Vejam o capitulo 3 - Programmer's reference do arquivo dui…pdf que contem codigo em C para o acessos dos leds, display de 7 segmentos e dip switches. Para o planejamento serah importante observar esse codigo e transpor para o assembly do ARM. O documento UMKS … eh o data sheet do ARM produzido pela Samsung. Os enderecos dos registradores estao no item 7.4.1 do ARM Lab Manual.

Se tiverem duvidas, perguntem.

Dica: Voce farah algo da seguinte forma para lidar com registradores. Como eles estao mapeados em memoria, acessamos os registradores como se estivessemos acessando a memoria normal.

- Escrevendo 0xf0 no IOPMOD:
ldr     r0, =0x3ff5000  @ IOPMOD
ldr     r2, =0xf0       @ seta 1 nos bits [7:4]
str     r2, [r0]        @ seta IOPMOD como output

7.5.1 - Escrita nos LEDS Displaying the hex digits in binary to the surface-mounted LEDs Write ARM assembly to flash the hex digits in binary form to the surface-mounted LEDs in ascending order. Now slightly modify the code to flash the digits in descending order. Make sure to use a delay so that the digits can be seen. The digits should not stop flashing.

7.5.3 - Escrita no display de 7 segmentos Displaying the contents of a memory location to the seven-segment display Write ARM assembly to inspect memory location 0x4000. If the location contains a decimal number in the range 0-15, display the contents in hex on the seven-segment LED display. As an example, if 0x4000 contains 14, display an E.

7.5.5 - Leitura dos dip switches Displaying the value of the DIP switches to the surface-mounted LEDs Write ARM assembly to inspect DIP1 to DIP4, which act like four binary digits. Display the contents in binary on the surface-mounted LEDs. See Figure 2-10 of Evaluator-7T User Guide for bit assignments

9.2 OBJETIVO

Faremos os exercicios 7.5.1 a 7.5.11 envolvendo entrada/saida.

9.3 Observacoes

  • Conexão com a placa evaluator 7t
    • ttyUSB0 -> ttyS0
      Para realizarmos a conexão com a placa, observe que no laboratório os computadores não possuem interface serial e sim uma interface USB/serial. O linux enxerga essa interface como o dispositivo /dev/ttyUSB0 Porém, o gnuarm 4.3 não enxerga /dev/ttyUSB0 e sim /dev/ttyS0. Por isso, execute o seguinte comando:
      su professor
      cd /dev
      sudo rm ttyS0
      sudo ln -s ttyUSB0 ttyS0
      

      Altere as permissoes de ttyUSB0 para que todos possa acessar:

      sudo chmod 666 ttyUSB0
      

      Ao usar o arm-elf-gdb, faremos a conexao com a placa da seguinte forma:

      This GDB was configured as "--host=i686-pc-linux-gnu --target=arm-elf"...
      (gdb) set remotebaud 57600    --> o default eh 9600 bps; se quiser, altere para  19200 38400 57600 ou 115200 caso deseje uma comuniacao mais veloz.
      (gdb) target rdi /dev/ttyS0
      Angel Debug Monitor V1.31 (ARM, INC. Evaluator Release v1.01)
      Built for ARM7TDMI Serial, IRQ
      Built Sep 26 2000
      Serial Rate:   9600
      RDI_info: internal error, unimplemented message
      Connected to ARM RDI target.
      

      Após conectar, nao esqueca do comando load. Use agora o arm-elf-gdb como em experiencias anteriores.

  • Área de Trabalho
    Nao rode o arm-elf-gdb dentro do diretório "Área de Trabalho" pois ele não conseguirá lidar com espaços no path.
  • Continue x run
    Apos o load, pode-se colocar breakpoints. Para rodar o codigo no kit, use c/continue e nao r/run.
  • Shoud reset the target
    Se após o comando target rdi /dev/ttyS0 a placa pedir para resetar o target, isso significa apertar o botao de reset 'sys' da placa evaluator7t.
  • Problema ao usar o run - o gdb pergunta se quer reiniciar o programa.
    Use continue ou c ao inves de run, para continuar a execucao. Ao invés de:
    b main
    run
    

    faça

    b main
    c
    

    onde c = continue.

  • vc. jah conectou uma vez, mas nao consegue se conectar uma segunda vez.
    Provavelmente o problema estah em que o arm-elf-gdb estah segurando a porta serial. No terminal, supondo que o arm-elf-gdb nao esteja rodando, faca:
    ps ax | grep arm-elf-gdb
    

    Se tiver algum processo arm-elf-gdb segurando a porta serial, faca:

    killall -9 arm-elf-gdb
    

    e tente novamente a conexao.

  • Problema no STR
    As portas de entrada e saida possuem tamanhos fixos: byte, word. Nao eh possivel fazer um STR (word) em uma posicao onde a porta eh um byte. Para isso use STRB.
  • Problema no LDR ou ADR em posicoes como 0x3000, 0x4000
    Nao use posicoes fixas como 0x3000 pois a placa evaluator7t deve ter essa posicao em EPROM ou alocada para o programa monitor. Declare um vetor ao final do seu codigo, como em http://www.coranac.com/tonc/text/asm.htm:
        mov     r2, #1
    @ Byte loads
        adr     r0, bytes
        ldrb    r3, bytes       @ r3= bytes[0];     // r3= 0x000000FF= 255
        ldrsb   r3, bytes       @ r3= (s8)bytes[0]; // r3= 0xFFFFFFFF= -1
        ldrb    r3, [r0], r2    @ r3= *r0_b++;      // r3= 255, r0++;
    @ Halfword loads
        adr     r0, hwords
        ldrh    r3, hwords+2    @ r3= words[1];     // r3= 0x0000FFFF= 65535
        ldrsh   r3, [r0, #2]    @ r3= (s16)r0_h[1]; // r3= 0xFFFFFFFF= -1
        ldrh    r3, [r0, r2, lsl #1]    @ r3= r0_h[1]? No! Illegal instruction :(
    
    @ Byte array: u8 bytes[3]= { 0xFF, 1, 2 };
    bytes:
        .byte   0xFF, 1, 2
    @ Halfword array u16 hwords[3]= { 0xF001, 0xFFFF, 0xF112 };
        .align  1    @ align to even bytes REQUIRED!!!
    hwords:
        .hword  0xF110, 0xFFFF, 0xF112
    
  • 7.5.3, 7.5.4
    Em 7.5.3 estah escrito para usar a posicao 0x4000 de memoria. Ao inves disso, declare o que for preciso de dados no codigo em assembly como na observacao imediatamente acima. Nao use a posicao 0x3000 - o evaluator7t deve estar usando tambem essa posicao - por exemplo, ao se comunicar com o arm-elf-gdb. Os valores escritos na memoria em 0x3000 serao alterados pela placa e nao pelo seu programa. Veja observacao acima.
  • 7.5.6
    The program must be stopped manually. Nao eh necessario que se pare o programa manualmente.

9.4 Envie o codigo fonte e possiveis comentarios dos exercicios no final da aula via email, subject: labmicro cap 8.

10 2016-07-14 . C compiler + assembler - juntar C com assembly.

10.1 PLANEJAMENTO:

Crie um programa em C (imprime.c) que faz uma contagem de 5 a 0 usando uma funcao recursiva do tipo:

main() {
     imprime(5);
}

imprime(N) {
  if (N<0) {
      exit();
  }
  printf("numero = %d\n", N);
  delay();
  imprime(N-1);
}

Crie de fato o programa em C e compile em casa antes da aula. Faca o compilador gerar o código assembly imprime.s através de

arm-elf-gcc –S imprime.c

Estude o código e simule a pilha em uma folha de papel escrita a mão:

  • Como a funcao imprime estah usando a pilha para fazer a chamada recursiva?

Apresente ao professor também o código imprime.c e imprime.s no comeco da aula na tela do computador (ou seja, pode ser de forma eletronica). Apresente a explicacao de como a pilha estah sendo usada (simule desenhando a pilha usando o fp, ip, sp) para fazer a chamada recursiva numa folha de papel escrita aa mao.

Referencias que podem ajudar:

http://www.cl.cam.ac.uk/~fms27/teaching/2001-02/arm-project/02-sort/apcs.txt

http://msdn.microsoft.com/en-us/library/ms253599%28v=vs.80%29.aspx

http://stackoverflow.com/questions/15752188/arm-link-register-and-frame-pointer

10.2 Objetivos da aula de hoje:

  1. compilar codigo assembly e codigo C e linkar ambos.
  2. alterar o codigo C inserindo assembly no meio do código C.
  3. observar como funciona a recursao e gerar um relatorio usando o libreoffice com os printscreens da tela do arm-elf-gdb.

RELATORIO: Excepcionalmente para a aula de hoje, ao inves de enviarem um texto por email, crie um documento usando o libreoffice ou o openoffice anexando os printscreens com uma explicacao do que ocorre ao seguirem os itens abaixo. Como diversas instrucoes alteram a pilha, vc. pode observar a pilha fazendo: x/16 $sp

Pedi para uma equipe em 2011 gerar uma apostila sobre isso: http://www.pcs.usp.br/~jkinoshi/2012/usandoC-ProjetoLabProc.pdf

Nessa apostila existe o código de sement.c e segment.h, mas voce pode acha-los em

http://www.pcs.usp.br/~jkinoshi/2014/segment.c

http://www.pcs.usp.br/~jkinoshi/2014/segment.h

Vamos seguir a experiencia observando os itens abaixo que fazem referencia aa apostila. A medida que seguirem os itens abaixo, preencham o relatorio para ser enviado no final da aula.

10.2.1 compilar codigo assembly e codigo C e linkar ambos.

  • O professor passou segment.c, segment.h. Gere o arquivo segment.o e execute no kit. Veja que aparece um numero no display. Note que segment.c imprime o numero no main. Ver PASSO-1 da apostila.

A apostila foi feita para se rodar a experincia na placa evaluator-7t

arm-elf-gcc segment.c –o segment.o

porém, para debugar voce precisa adicionar a opcao -g

arm-elf-gcc -g segment.c –o segment.o
  • crie a funcao imprime em segment.c que recebe um numero e coloca esse numero no display. Para isso basta retirar do main a parte pertinente. Compile e teste. Ver PASSO-2 da apostila.
  • crie o arquivo imprime.c com a funcao imprime gerada no item anterior. O codigo executavel eh gerado atraves de: arm-elf-gcc -g imprime.c segment.c -o segment.o; teste o codigo gerado. Ver PASSO-3 da apostila.
  • compile imprime.c gerando seu objeto: arm-elf-gcc -S impime.c (gerando imprime.s). Refaca o codigo usando imprime.s + segment.c. Teste. Ver PASSO-4 da apostila.

No passo 4, existem duas formas de gerar o arquivo .elf para ser executado na placa:

a) O gcc faz tudo (monta e linka): arm-elf-gcc –g imprime.s segment.c –o segment.o

b) O gcc soh compila:

Para o segment.c e para o imprime.c fazer:

arm-elf-gcc -S segment.c
arm-elf-as -o segment.o segment.s

E depois linkar o segment.o e imprime.o usando arm-elf-ld. Porém utilize a forma a) para evitar problemas com linkar com a biblioteca do ambiente (para uma funcao usada para chamar o main e para o printf).

10.2.2 alterar o codigo C inserindo assembly no meio do código C.

  • Estude o codigo imprime.s.
    Como o numero eh passado como parametro? Como o fp é usado para isso? Isso corresponde ao PASSO-5 da apostila, mas não é necessário fazer esse passo.

    Em uma funcao, os registradores poderiam ser salvos e recuperados chamados assim:

    BL myfunction
    myfunction
               .....
               STMFD sp!, {r4-r10, lr}; guarda os registradores
               .....
               .....
               LDMFD sp!, {r4-r10, pc}; recupera os registradores; o retorno da funcao eh feito colocando lr em pc.
    

    Para entender melhor como funciona a chamada de rotinas, veja: http://infocenter.arm.com%2Fhelp%2Ftopic%2Fcom.arm.doc.ihi0042d%2FIHI0042D_aapcs.pdf Responda: Quais sao os registradores atribuidos a: fp, ip, sp, lr?

    Para que serve o fp? Responda.

    Para que serve o ip? Resposta: Register r12 (IP) may be used by a linker as a scratch register between a routine and any subroutine it calls (for details, see §5.3.1.1, Use of IP by the linker). It can also be used within a routine to hold intermediate values between subroutine calls.

  • Agora que voce conhece imprime.s e imprime.c; altere imprime.c para se colocar codigo assembly dentro do codigo C usando o inline.
    Ver PASSO-6 da apostila. Observacao: O compilador gera lables com pontos na frente (ex: .L4). Apenas o compilador deve fazer isso. O código a ser inserido inline não deve ter labels comecando por ponto (ex: L4). Isso evita que o compilador e o usuário criem e usem labels iguais.

10.2.3 observar como funciona a recursao.

  • Observe como o imprime do item 1.2 imprime um caracter no codigo assembly (gerado ao compilar .c com -S).
    Utilizando o gdb observe como o fp, ip e sp são utilizados em:
    stmfd sp!, {fp, ip, lr, pc}
    

    e como sao desempilhados em:

    ldmfd sp, {r3, fp, sp, pc}
    

    O arm-elf-gcc estah primeiro passando os parametros por registradores e depois empilhando-os dentro da rotina, por isso os parametros sao acessado via [fp - Numero] pois foram empilhados depois da entrada da rotina que empilhou ip, sp, fp. Supondo que uma funcao tenha 5 parametros, a funcao terah que obrigatoriamente empilhar alguns antes da chamada da rotina e esses parametros vao ser acessados via [fp + Numero]. Verifique colocando 5 parametros na funcao recursiva. Observe como eles sao empilhados e acessados.

    Observe que entre o stmfd e o ldmfd, o sp é alterado; por isso lr eh repassado para o pc.

    O grupo do Lucas,Ricardo,Gabril,Jonatas enviaram uma foto de como a pilha se comporta quando nas chamadas recursiva em: pilha.PNG

    Perguntas: Como o parametro é passado para imprime? Na resposta explique o caso da rotina ter 5 parametros (via registrador e pilha) e da rotina ter poucos parametros (via registrador).

    Como esse parametro é empilhado (isso é necessário em caso de chamadas recursivas)? Como é aberto um espaco na pilha para o parametro de imprime? Onde isso é feito no código?

    Por que se faz fp-16 para acessar o parametro?

    Como esse parametro é desempilhado? Observe que o ip eh repassado para sp e com isso, a alteracao na pilha para abrir o espaco para o parametro de "imprime" é automaticamente refeito.

    • Declare a variavel local "int lixo" na funcao imprime.
      Dentro da funcao recursiva faca, lixo++. Observe o codigo assembly gerado pelo compilador. Responda no relatorio: Como lixo foi referenciado? Como foi aberto espaco na pilha para o lixo? Retire printscreens comparando a pilha sem o uso do int lixo e com o uso de int lixo. Variaveis locais devem ser empilhadas pois caso, a funcao seja recursiva elas fazem parte da recursao.
  • Altere imprime em imprime.c para que imprima 7 numeros de 1 a 7 (sem usar assembly no inline).
    Gere o codigo imprime.s. Estude como o fp (frame pointer) eh usado para marcar uma posicao na pilha empilhando parametros e variaveis locais. Apresente no relatorio como a pilha e o fp estao sendo usados. Corresponde ao PASSO-7 da apostila.

    Rode essa versao de imprime no gdb. Retire printscreen antes e depois de cada instrucao que faz alteracao na pilha, em particular:

    stmfd sp!, {fp, ip, lr, pc}
    ldmfd sp, {r3, fp, sp, pc}
    

    Pergunta: Quando imprime chama recursivamente imprime é necessário que o haja um ponteiro para o fp anterior. Como isso é feito? Quando imprime retorna para uma instancia anterior é necessário ue o fp retorne para servir de base para os parametros e variaveis locais anteriores. Explique como isso é feito.

    Ao final da aula, enviar email ao professor contendo o documento gerado com os printscreens. Para ver a pilha no gdb faca x/16 $sp

10.3 Envie possiveis comentarios dos exercicios no final da aula via email, subject: labmicro aula 9 e código fonte onde for necessário.

11 2016-07-21 . Hello World for bare metal

11.1 Planejamento:

daqui para o final do curso, vamos utilizar a placa versatile emulada (similar ao evaluator7t mas emulada pelo qemu); comecando por uma placa sem absolutamente nada de software (bare metal), ou seja, sem o software que faz o boot.

Leia: http://balau82.wordpress.com/2010/02/14/simplest-bare-metal-program-for-arm/

Lá lemos: In order to create a bare metal program we must understand what does the processor do when it is switched on. The ARM9 architecture begins to execute code at a determined address, that could be 0 (usually allocated to RAM) or 0xFFFF0000 (usually allocated to Read Only Memory). We must put some special code at that particular address: the interrupt vector table.

Pesquise e responda em uma folha de papel escrita a mão:

  1. Como o ARM9 decide se a primeira instrucao a executar está em zero ou em 0xFFFF0000? A resposta estah no site do ARM.
  2. Compile o código em casa antes da aula e tente rodar usando o arm-elf-… normal que estamos usando. Voce conseguiu observar o código usando arm-elf-gdb em casa?
  3. Pesquise na internet (manual do GNU) e responda na folha escrita a mão, o que faz test.ld?
ENTRY(_Reset)
SECTIONS
{
 . = 0x0;
 .text : {
 startup.o (INTERRUPT_VECTOR)
 *(.text)
 }
 .data : { *(.data) }
 .bss : { *(.bss) }
 . = . + 0x1000; /* 4kB of stack memory */
 stack_top = .;
}

O que significa em test.ld:

3.1 - ENTRY(_Reset)
3.2 - startup.o (INTERRUPT_VECTOR)
3.3 - stack_top = .;
3.4 - .bss : { *(.bss) }
3.5 - . = . + 0x1000; /* 4kB of stack memory */

11.2 Experiencia

Como a placa Versatile (similar ao evaluator7t do laboratorio) pode imprimir um "hello world" ao bootar? O processador ARM, ao ser ligado, passa a executar código a partir da posicao zero. Para isso precisamos de gravar uma EPROM que contenha um jump na posicao zero para a rotina que imprime o hello world. Como não temos nenhum software, é necessário criar a rotina que faz a impressao da string via serial. Para essa experiencia, leia:

http://balau82.wordpress.com/2010/02/14/simplest-bare-metal-program-for-arm/ Nesse post, o Balau explica como fazer a rotina Centry que nao contem nada ser executada como se uma placa com o processador ARM9 estivesse sendo bootada. Ele mostra como fazer isso no simulador.

http://balau82.wordpress.com/2010/02/28/hello-world-for-bare-metal-arm-using-qemu/ Nesse post, o Balau explica como se imprime "Hello World" de uma Versatile emulada pelo qemu; porém com o código rodando a partir de 0x1000 e não a partir do zero como seria de se esperar do "bare metal" de fato.

http://eetimes.com/design/embedded/4007119/Building-Bare-Metal-ARM-Systems-with-GNU-Part-1–Getting-Started Um excelente artigo explicando como colocar software sobre o hardware usando GNU.

Realize os passos abaixo para a aula de hoje. Em paralelo, construa um relatorio a ser enviado com o subject "labmicro aula 10" no fim da aual. Anexe código e printscreens do qemu, principalmente ao se constatar a mudanca de modo de processamento.

Para a aula faça:

11.2.1 Instale o arm-none-eabi

Antigamente essa toolchain estava mais livre. Agora, estao pedindo que se cadastre para fazer o download. Uma possivel forma de diblar isso é: http://fun-tech.se/stm32/CodeSourceryInstall/ O importante é ter o arquivo arm-none-eabi.bin para fazer a instalacao. Exemplo: http://www.pcs.usp.br/~jkinoshi/2014/arm-2011.03-42-arm-none-eabi.bin

11.2.2 Rode o Simplest Bare Metal Program

Siga os passos em http://balau82.wordpress.com/2010/02/14/simplest-bare-metal-program-for-arm/ e rode esse programa. O interessante é o startup.s que contém a inicializacao do vetor de interrupcao e faz o codigo rodar a partir da posicao zero.

11.2.3 Rode o Hello World

Veja http://balau82.wordpress.com/2010/02/28/hello-world-for-bare-metal-arm-using-qemu/ e observe que o código passa a executar a partir de 0x10000 e não a partir do zero. Provavelmente o Balau fez dessa forma, porque toda placa vem com um código em eprom que deve ser emulado que corresponde justamente ao código de boot. Para que isso seja possível, use test.ld (o ldscript) e statup.s em http://balau82.wordpress.com/2010/02/14/simplest-bare-metal-program-for-arm/ para fazer o código rodar a partir de 0x0 como acontece em uma placa real. Observe:

  • o startup.s desse post contém o vetor de interrupcao na posicao zero.
  • (CUIDADO) No arm..gdb, execute o "load" após o "target" .

Coloque um breakpoint antes de imprimir o "Hello World" e analise o cpsr. Qual é o modo em que o processador executa? Verifique se está em "Supervisor Mode". Qual é o valor de sp?

Consulte o cpsr em:

no capitulo 1, item 1.6.4

Voltando para http://balau82.wordpress.com/2010/02/28/hello-world-for-bare-metal-arm-using-qemu/, crie a funcao Undefined em C que imprime a string "instrucao invalida!" e fique em um loop infinito. Pendure essa funcao no vetor de interrupcao na posicao correspondente a undefined instruction. Observe que dentro do gdb é necessário executar o "load" após o "target".

11.2.4 instrucao invalida em startup.s

Coloque uma instrucao invalida em startup.s (com o startup.s inicializando o codigo em 0x0). Para isso:

Reset_Handler:
        LDR sp, =stack_top
        BL c_entry
        .word 0xffffffff
        B .

Rode o test.elf com a instrucao invalida. Observe se a string eh impressa. Eh possivel que ocorra um erro e uma string totalmente errada seja impressa, ou que o qemu trave pois o ponteiro de pilha r13 no modo undefined nao foi inicializado.

Coloque um breakpoint antes e depois da instrucao invalida e analise o cpsr. Tente observar a mudanca de modo do processador. Conseguiram? Se sim, observe a mudanca no registrador sp (provavelmente o sp no modo Undefined está zerado ou com um valor qualquer. Verifique isso; portanto, qualquer alteracao de pilha nesse modo pode levar a erros com o sp errado). Se nao conseguiram, vejam o Undefined Handler no proximo item e observem a mudanca de modo dentro do Undefined Handler.

11.2.5 Um Undefined Handler simples, porem errado.

Pendure o seguinte UndefinedHandler no vetor de interrupcao em startup.s (certifique-se de que o ld script coloca o codigo em 0x0):

Undefined_Handler:
        LDR sp, =stack_top
        BL undefined

Coloque um breakpoint dentro do UndefinedHandler para observar a mudanca de modo no registrador de status.

Existem 2 erros nesse UndefinedHandler

  • a pilha nao deve ser inicializada a cada entrada na rotina de excessao. Veja o proximo item - a pilha no Undefined mode.
  • o retorno estah errado.

11.2.6 A pilha no Undefined mode.

Quando o processador chaveia de modo, o registrador r13 de um modo não é o mesmo registrador r13 que no outro modo; por isso é necessário inicializar os sp's de todos os modos logo no reset da placa como visto na aula passada. Veja o item 1.6 "The ARM register set" da apostila de 1 e observe se existem outros registradores que variam conforme o modo do processador. Para evitar problema no uso da pilha no modo Undefined, é necessário que ResetHandler inicialize também o ponteiro de pilha R13UNDEF (registrador r13 no modo undefined). Inicilize R13UNDEF com o valor 0x2000 e inicialize o R13SVC (do supervisor) em 0x1000. Para isso é necessário utilizar a instrucao MSR (Move to Status Register). Usando MSR vah para o modo undefined e altere o R13. Esse R13 no modo undefined (R13UNDEF) eh diferente do R13 no modo supervisor. Depois volte ao modo supervisor. Descubra o número a ser carregado em MSR (observando principalmente o modo e desabilitando as interrupcoes.

Pergunta:

  • A pilha do Undefined eh inicializada onde?

resposta:

  • ResetHandler pois a pilha do Undefined nao deve ser alterada logo na entrada do UndefinedHandler - o erro eh semelhante a alterar o topo da pilha ao chamar uma funcao!

11.2.7 Undefined handler

Refaça o item anterior observando se sp é alterado corretamente na troca de modos.

Embora existam diversos modos no ARM, podemos classificar em usuário e o resto. Quando o ARM comeca a executar está em modo supervisor. Passe para o modo usuário usando MSR e tente voltar do modo usuário para supervisor também usando MSR. O que acontece? Por quê? Refaca a experiencia trocando entre modos dentro do resto (undefined, abort, supervisor, etc.). O que acontece? É possível concluir então que existem dois grandes modos: usuário e supervisor?

Crie o undefined handler em assembly de forma que ele salve e recupere os registradores. Veja:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0471c/Ciheidgb.html

Explique como o processador volta para o modo anterior ao sair do undefined handler. Rode o programa passo a passo observando os registradores e cpsr sendo salvos e recuperados.

A instrucao:

LDMFD sp!,{R0-R12,pc}^

serah a ultima a ser executada pelo undefinedhandler. Ela deve fazer com que o pc continue a partir da instrucao indefinida com o modo anterior (no caso supervisor). Como essa instrucao faz isso?

Responda:

  • por que tem um chapeuzinho no final da instrucao? Para que serve isso?
  • por que essa instrucao nao salva os registradores r13 e r14?
  • se essa eh a primeira instrucao a ser executada, o sp jah deve ter sido inicializado. Quem fez isso? (voce jah deve ter feito isso logo quando a placa eh incializada usando a instrucao MSR para chavear o modo e inicializar o sp).

Antes de escrever a instrucao de store que salva os registradores no comeco do undefinedhandler coloque um breakpoint na entrada do undefinedhandler e veja onde estah o endereco de retorno. Estah na pilha ou no registrador LR? Agora acerte o undefinedhandler com as instrucoes que armazenam e recuperam o estado da pilha.

11.3 Envie o codigo fonte e possiveis comentarios dos exercicios no final da aula via email, subject: labmicro aula 10.

12 2016-07-28 . interrupcao de tempo no Versatile emulado.

12.1 Planejamento:

– Comente as partes em que o código está dividido e o que faz cada parte. – Como o timer eh programado? Como se define o intervalo entre interrupcoes? – Para que servem os registradores do timer e onde sao utilizados no programa (inicializacao no progama principal ou interrupt handler)? TIMER0L - load TIMER0V - value TIMER0C - control TIMER0X - clear – Para que servem os registradores da controladora de interrupcao e onde sao utilizados no programa (inicializacao no progama principal ou interrupt handler)? INTEN - enable INTPND - status INTSEL - select (FIQ, IRQ)

– O que significa:

LDR r0, INTEN
LDR r1,=0x10 @bit 4 for timer 0 interrupt enable
STR r1,[r0]

12.2 Experiencia:

No ano de 2011, a equipe do Lucas Estevam fez o seguinte documento: http://www.pcs.usp.br/~jkinoshi/2012/exp-int-versatile.pdf Este relatório possui várias coisas interessantes:

  1. qemu emulando uma placa versatile sem nada - sem linux, etc.
  2. o firmware que programa a interrupcao de timer da placa, cria o vetor de interrupcao, faz o vetor de interrupcao apontar para a rotina de tratamento de interrupcao, dispara o timer de forma a gerar a interrupcao. A rotina de interrupcao limpa o pedido de interrupcao.
  3. o ldscript permite que aloquemos código em posicoes fixas, em particular, na posicao definida pela ARM para o vetor de interrupcao.
  4. Eles nao estao usando o tootle chain que vimos na aula passada arm-none-eabi e sim o que usamos ao longo das aulas que foi o arm-elf. Ao lerem a apostila usem sempre o arm-none-eabi e em especial, arm-none-eabi-ld.

O objetivo da aula é:

  • observar o vetor de interrupcao definido pela ARM.
  • observar como é feita a programacao do timer.
  • observar como as interrupcoes sao habilitadas.
  • observar como o pedido de interrupcao eh abaixado na rotina de interrupcao.
  • observar como funciona o modo usuario e o modo supervisor e consequentemente como as pilhas de usuario e supervisor sao utilizadas.

Na aula:

  • Crie um relatorio em texto para ser enviado via email com o subject "pcs2031 - lab11" no final da aula:

12.3 Geracao do código

  • Gere o código usando arm-none-eabi. Nao use arm-none-linux-eabi e qualquer outro toolchain.

12.4 Observacao sobre o codigo

12.4.1 teste do INTPND

TST r0, #0x0010 @verifica se é uma interupção de timer BNE handlertimer @vai para o rotina de tratamento da interupção de timer

A instrucao TST faz o ANDS bit a bit e seta a flag Z (usada pelo BNE) caso o resultado seja zero. O importante eh olhar se o bit estah setado. Se estiver, entao o resultado nao eh zero e por isso eh feito o desvio para o handlertimer.

12.5 Corrija os ERROS na apostila

12.5.1 O código da apostila não inicializa adequadamente as pilhas na placa versatile

As pilhas no modo supervisor e no modo IRQ nao sao inicializadas. Com base na experiência da aula passada, ajuste isso; usando MSR. Se voce nao fizer isso, terah problemas graves ao ocorrerem as interrupcoes porque o codigo nao vai retornar para a posicao certa antes da interrupcao.

12.5.2 O retorno de IRQ é feito de forma ERRADA

Observe o codigo

do_irq_interrupt: @Rotina de interrupções IRQ
   STMFD sp!, {r0 - r3, LR}
   @Empilha os registradores
   LDR r0, INTPND @Carrega o registrador de status de interrupção 
   LDR r0, [r0]
   TST r0, #0x0010 @verifica se é uma interupção de timer
   BNE handler_timer @vai para o rotina de tratamento da interupção de timer
   LDMFD sp!, {r0 - r3,lr}
   mov pc, r14
   @retorna

O retorno do IRQ está errado em:

mov pc, r14

r14 é o LR. O LR é automaticamente setado pelo processador ARM quando ocorre a IRQ; porém da seguinte forma: LR = enderecoderetorno + 4 devido ao pipeline. Para resolver isso é necessário fazer retirar 4 de LR.

  • Obs - logo ao entrar na rotina de interrupcao, observe para qual instrucao o LR aponta atraves de:
x/i $lr

depois de subtrair 4 de LR, observe novamente

x/i $lr

Agora faz sentido? Isso ocorre por que a propria ARM define assim; meu chute eh pelo pipeline que andou no irq, mas nao andou no undefined (visto na aula passada).

12.5.3 Problema ao recuperar o cpsr anterior

As duas instruções:

LDMFD sp!, {r0 - r3,lr}
mov pc, r14

não recuperam o cpsr anterior. Isso acarreta um problema. O CPSR contém um bit que diz se o processador atende as interrupcoes IRQ ou não. Logo quando ocorre a interrupcao o bit I do cpsr eh zerado para desabilitar que uma interrupcao ocorra dentro de outra. Como o CPSR nao retorna ao valor anterior, ele permanece com as interrupcoes desabilitadas. Com base nessa informacao, altere o código de forma a que a rotina de interrupcao IRQ TERMINE com:

LDMFD sp!,{R0-R12,pc}^

como vimos na aula passada e explicado em http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0471c/Ciheidgb.html

  • Obs1: vários alunos trocaram a instrucao LDMFD sp!,{R0-R12,pc}^ por algo como LDMFD sp!,{R0-R12,lr}^ e depois alteraram o pc. Isso nao funciona pois o modo de operacao (e pilhas e backed registers) jah foram alterados. Eh obrigatorio executar LDMFD sp!,{R0-R12,pc}^ para retornar da rotina de interrupcao.
  • Obs2: Se sua equipe nao estiver conseguindo retornar da interrupcao rodando LDMFD sp!,{R0-R12,pc}^ observe a pilha. Coloque um breakpoint nessa instrucao e examine como estah a pilha observando o sp. Os valores que estao na pilha correspondem aos que serao colocados nos registradores, e em especial no pc? O retorno deve ser para o loop infinito do programa principal. Esse endereco de retorno estah de fato na pilha? Ao observar o endereco de retorno na pilha, voce pode observar a instrucao que serah executada atraves do comando do debugger x/i ENDERECO.

12.5.4 Polemica - BNE x BLNE

Alguns alunos consideraram que o mais correto eh usar BLNE ao inves de BNE. Isso nao eh necessario, depende de como handlertimer retorna. A ideia da apostila era retornar para o loop do programa principal diretamente, portanto nao cosideramos isso como um erro da apostila. Contudo, o codigo da apostila nao estah elegante. O melhor eh fazer como no codigo abaixo usando BLNE e fazer handlertimer retornar para quem chamou ao inves de fazer LDMFD.

do_irq_interrupt: @Rotina de interrupções IRQ
   STMFD sp!, {r0 - r3, LR}
   @Empilha os registradores
   LDR r0, INTPND @Carrega o registrador de status de interrupção 
   LDR r0, [r0]
   TST r0, #0x0010 @verifica se é uma interupção de timer
   BLNE handler_timer @vai para o rotina de tratamento da interupção de timer
   LDMFD sp!, {r0 - r3,lr}
   mov pc, r14    @retorna

12.6 Observe e faça pequenas alteracoes no codigo em assembly

  • debugue o codigo: rode até a instrucao LDMFD sp!,{R0-R12,pc}^ e examine a pilha. Verifique se o valor a ser carregado em PC está correto.
  • Observe o código de inicializacao:
timer_init:
 mrs r0, cpsr
 bic
 r0,r0,#0x80
 msr cpsr_c,r0 @enabling interrupts in the cpsr
 LDR r0, INTEN
 LDR r1,=0x10 @bit 4 for timer 0 interrupt enable
 STR r1,[r0]
 LDR r0, TIMER0C
 LDR r1, [r0]
 MOV r1, #0xA0 @enable timer module
 STR r1, [r0]
 LDR r0, TIMER0V
 MOV r1, #0xff @setting timer value
 STR r1,[r0]
 mov pc, lr
  • Qual é a instrucao que habilita as interrupcoes? Desabilite as interrupcoes no cpsr e rode. O que acontece? Voce acha razoavel habilitar as interrupcoes enquanto se programa o timer? De fato isso nao eh razoavel, primeiro deve-se programar o timer para depois habilitar as interrupcoes. Isto eh: alterar cpsr zerando o bit I deve ser a ultima operacao a ser feita. Altere isso no codigo. Veja se funciona.
  • Se tudo estiver correto, anexe esse código no email a ser enviado ao professor.
  • Como o código definiu o vetor de interrupcao? Responda relacionando com o ARM e o ldscript.
  • Como foi feita a programacao do timer? Altere os valores que estao sendo programados e observe o que acontece. É
  • Qual é o modo em que o processador roda logo no início enquanto o timer está sendo configurado? Qual registrador deve ser observado? Qual é sp utilizado?
  • Coloque um breakpoint logo no início da interrupcao de timer e rode até lá. Qual é o modo em que o processador roda? Qual é o sp? O que se vê na pilha? Como o PC é salvo na chamada da interrupcao?
  • Como o processador chaveia de modo ao sair da rotina de interrupcao? Verifique isso localizando a instrucao que faz isso. Quando estamos debugando passo a passo e observamos que o processador sai da rotina de interrupcao, pode acontecer do timer da maquina virtual continuar marcando o tempo e como nós seres humanos somos muito mais lentos que a máquina, é bem provável que logo ao sair da rotina de interrupcao, já tenha ocorrido outra interrupcao de timer. Verifique se isso acontece.

12.6.1 Fazendo o tratamento da interrupcao em C.

O código está todo em assembly, mas é possível codificar parte dele em C. Para isso precisamos de seguir diversos passos:

  • observe o código de handlertimer:
handler_timer:
        LDR r0, TIMER0X
        MOV r1, #0x0
        STR r1, [r0] @clear timer interrupt

        @ do whatever you want with the timer here

        LDMFD   sp!, {r0 - r3,lr}        
        mov pc, r14

A instrucao mov pc, r14 corresponde ao retorno de doirqinterrupt (feito de forma errada) e não de handlertimer.

  • Altere o código assembly para que handlertimer seja uma rotina estanque chamada em doirqinterrupt através de:

BLNE handlertimer; ou seja, handlertimer deverá se comportar como uma rotina normal que retorna para quem chamou. Verifique usando o qemu+gdb se voce consegue entrar na rotina handlertimer.

  • Crie dois arquivos assembly irq.s e handler.s contendo handlertimer; verifique se funciona, observando duas ou mais interrupcoes de relogio e observe se o sp não está variando a cada chamada - se não está gradativamente empilhando coisas.

Observe que ao linkar, a ordem com que se colocam os objetos irq.o e handler.o eh extremamente importante. Deve-se colocar primeiro o codigo que contenha o vetor de interrupcao.

  • Depois recodifique handlertimer em C. Ao fazer isso, declare um pointer em C referente a TIMER0X de forma a colocar o valor zero nessa posicao de memoria a fim de baixar o pedido de interrupcao. Nao eh preciso aproveitar a definicao de TIMER0X do assembly; eh muito mais facil declarar o ponteiro direto em C.

Obs: para exportar o nome handlertimer eh necessario usar .global

  • Misture o codigo dessa experiencia com a da aula passada a fim de que o programa imprima uma vez "Hello World" no program principal, antes mesmo de programar o timer. Ao ativar o qemu retire a opcao que "mata" a serial: -serial /dev/null, pois caso contrário, voce não verá os caracteres na tela.
  • Usando a experiência passada, faça com que o handlertimer em C imprima o digito "1" a cada interrupcao.
  • Altere o programa principal para que ele fique em um loop continuamente imprimindo o digito "2" de tempo em tempo (fique em um loop para gastar tempo). Assim, na tela voce deverá ver "1"s e "2"s intercalados. Tente acertar esses tempos de tal forma que se imprima pelo menos 10 "2"s para cada "1".
  • O que acontece se nao retirarmos o pedido de interrupcao na rotina que trata a interrupcao? Experimente deixando o programa rodar e explique. A frequencia com que os caracteres sao impressos varia se nao retirarmos o pedido de interrupcao? Ou seja, a relacao entre "2"s e "1"s foi alterada? Explique no relatorio. Rode o programa de uma vez sem breakpoints. Em pelo menos uma equipe, aconteceu do resultado ser diferente rodando passo a passo pois nesse caso, o 1 e 2 eram intercalados enquanto que rodando de uma vez o resultado era o esperado (somente a rotina de interrupcao imprimia).

IMPORTANTE: Nao saia da aula sem apresentar os '1's e '2' sendo impressos.

12.7 Envie o codigo fonte e possiveis comentarios dos exercicios no final da aula via email, subject: labmicro aula 11.

13 2016-08-04 . Chaveamento entre 2 processos.

instalem o arm-none-eabi no computador de voces e rodem o codigo das 3 ultimas experiencias.

13.1 Planejamento:

Escreva o codigo de um programa numa folha de papel a mao que salva e recupera todos os registradores, inclusive PC, SP, LR e CPSR do processo que sofreu a interrupção. Para isso,

  • caso seu grupo nao tenha rodado a experiencia da aula passada, continue ateh ver os "1"s (da interrupacao) e "2"s (do programa principal) intercalados.
  • Crie uma estrutura de dados (um espaco na memoria), linhaA, onde voce deverah salvar todos os registradores r0-r12 ao entrar e sair da rotina de interrupcao. Rode e teste isso em casa.
  • Aumente essa estrutura de dados para armazenar os outros registradores: PC do programa principal, LR do programa principal, SP do programa principal e CPSR do programa principal. Os outros registradores LR, SP, PC e CPSR provém de fonte diferente (veja: http://aelmahmoudy.users.sourceforge.net/electronix/arm/chapter3.htm)
  • - CPSR/supervisor está salvo em SPSR/IRQ.
  • - PC/supervisor está salvo em LR/IRQ; melhor dizendo: PC/supervisor = LR/irq - 4

Obtenha esses registradores e armazena em linhaA. Uma ideia eh, logo ao entrar na interrupcao de relogio, salvar o PC do processo, ou seja, salvar LR/IRQ -4 (pode ser em memoria numa variavel declarada por voce), liberando LR para outros usos.

  1. - LR e SP são banked registeres (ver pag 1.7 da apostila Lab Manual).

Para obter o LR e o SP do modo supervisor use as instrucoes que alteram o modo do processador MRS R0,CPSR ; or SPSR MSR CPSR,R0 ; or SPSR

Tome cuidado ao usar MSR e MRS pois, ao chavear de modo, vc. pode acabar sujando registradores como o spsr; alem disso, lembre-se que o CPSR contem o bit I onde zero em I habilita as interrupcoes. Voce deverah alterar os modos para pegar LR e SP com as interrupcoes desabilitadas.

  • Reescreva o código de forma que na interrupcao de relogio, salve todos os registradores (incluindo LR, SP, PC, CPSR) e os recupere (como se fosse o chaveamento de um processo apenas).
  • Compile e execute o código em casa. Observe os 1s e 2s sendo impressos.

13.2 Objetivo:

  • Usar a interrupcao do timer para chavear entre duas tarefas. Crie uma tarefaA que continuamente imprime "1" e uma tarefaB que imprime "2".
  • Vamos criar um nanokernel que faz esse chaveamento:

– carregue o vetor de interrupcao. – No reset, faca a inicializacao da controladora de interrupcoes e do timer. – A rotina de interrupcao do timer deve fazer o chaveamento entre as tarefas; para isso ela deverá salvar o estado do processo corrente em uma estrutura de dados que vamos chamar de tabela de processos. Ao sair da rotina de interrupcao, os registradores do outro processo devem ser recuperados.

Para isso:

  • Rode o programa que voce trouxe no planejamento. Voce consegue ver todos os registradores salvos adequadamente? Consegue observar a pilha do supervisor e a pilha do IRQ?
  • Além de linhaA, declare o espaco linhaB para a taskB.
  • A rotina de interrupcao deve observar o processo que estah rodando. Se for a tarefaA rodando, salva o estado na linha da tabela de processos da tarefaA e recupera o estado da linha da tarefaB e de forma semelhante para a tarefaB. Para isso, declare a variavel nproc (global) que contem o numero do processo rodando (0 para A e 1 para B).
  • reserve uma area de pilha para cada processo. Isto eh: quando a taskA for rodar, ela deverah passar a rodar com o SP em um certo valor e quando a taskB for rodar, ela deverah passar a rodar com o SP em outro valor. Para isso voce deverah disparar a taskA em modo supervisor com seu SP e CPSR jah setados adequadamente pela inicializacao do programa principal; mas tanto para a taskA quanto para a taskB, deixe o SP, PC e CPSR correspondentes armazenados corretamente em linhaA e em linhaB.
  • O programa principal deixa de ser o loop infinito. A taskA jah sai rodando logo na inicializacao. De tal forma que quando ocorrer a primeira interrupcao de tempo, a taskB passa a rodar.
  • Para a taskB entrar rodando eh necessario que seus registradores estejam inicializados corretamente na estrutura de dados linhaB. Entre os valores principais para serem inicializados temos: PC, SP e CPSR. Inicilizar o CPSR de forma errada, com as interrupcoes desabilitadas, irah 'travar' tudo.
  • Ao rodar o programa, é de se esperar que saiam "1"s e "2"s intercalados.

13.3 Envie o codigo fonte e possiveis comentarios dos exercicios no final da aula via email, subject: lab micro aula 12.

14 2016-08-11 . Prova 2

14.1 TODO Prova

prova lab micro. A prova serah individual com o objetivo de verificar a capacidade do aluno de lidar com o ambiente que temos visto no laboratorio. Exemplo:

  • pedir ao aluno para entrar na rotina de interrupcao e mostrar qual a proxima instrucao que o processador iria executar se nao tivesse interrompido (para onde a rotina de interrupcao deve retornar).
  • pedir ao aluno para mostrar a pilha logo ao entrar na rotina de interrupcao. Essa pilha eh igual aa pilha que estava sendo usada? Se era diferente, como voce faria para localizar essa pilha?
  • antes da interrupcao, quais eram os registradores que estavam sendo usados? Como esses registradores devem ser salvos para guardar o estado do processo. Explique o motivo de como eles sao salvos.
  • onde o estado do processo eh salvo? A area de memoria onde o estado eh salvo estah declarada em C ou em assembly? Declare novamente em C (ou declare novamente em assembly).
  • mostre como o timer estah sendo usado.
  • coloque o programa principal em C.
  • o que aconteceria se nao baixássemos o pedido de interrupcao?
  • O que acontece se retirarmos o chapeuzinho ^ da instrucao que retorna da rotina de interrupcao? Alias, o que faz essa instrucao?

Além disso posso pedir na prova pequenas alteracoes no codigo como: 1 - Alem dos dois processos, insira um terceiro processo em C para rodar num codigo separado. 2 - faca a inicializacao da pilha do modo abort tambem e coloque um processo rodar no modo abort e outro processo no modo supervisor. 3 - Alem dos dois processos, insira um terceiro processo em assembly para rodar em codigo separado que imprime continuamente de 1 a 5.

4 - Altere um processo (pode ser um dos dois processos) inserindo uma instrucao indefinida e coloque para rodar. Coloque o tratamento para instrucao indefinida que imprime o numero do processo rodando que causa a excessao. 5 - faça com que o processo 1 execute duas vezes mais que o processo 2. 6 - coloque um processo que imprime de 1 a 6 fazendo com que o processo chame uma funcao recursiva. Pode ser um dos dois processos que jah estao rodando.

para a prova, jah venham com o codigo de chaveamento entre 2 processos funcionando. estudem bem as 3 ultimas experiencias: 10, 11, 12.

h

15 Avaliacao:

Nota Final = (Fase1 + 2*Fase2)/3

Fase1 = (E2+E3+E4+E5+E6)/5 * 0.2 + 0.8 * P1 Fase2 = (E8+E9+E10+E11+E12)/5 * 0.2 + 0.8 * P2

Avaliação por experiência:

  • Penalidade = -1 por atraso de 15 minutos
  • Penalidade = -4 por atraso de 1 hora.

A avaliacao será feita individualmente:

  • o aluno tem conhecimento do planejamento? Sabe explicar o código?
  • tem participado continuamente da aula; ou seja, os alunos tem se revezado no computador? Caso um aluno monopolize a aula, os outros serão penalizados.

Ao término de cada experiência enviar código para o professor.

15.1 Referências:

  1. ARM Laboratory Exercises - apostila - http://courses.cs.tamu.edu/rabi/cpsc617/resources/ARM%20Lab%20Mannual.pdf
  2. ARM Assembly Language Fundamentals and Techniques, William Hohl, CRC Press
  3. ARM System-on-Chip Architecture (2nd Edition), Steve Furber
  4. ARM System Developer’s Guide Designing and Optimizing System Software; Andrew N. Sloss, Dominic Symes, Chris Wright; Elsevier
  5. KinOS: http://code.google.com/p/arm7linux/downloads/list

Footnotes:

1 DEFINITION NOT FOUND: 1

Date: 2016-07-28 14:40:24 BRT

Author: Professor

Org version 7.8.02 with Emacs version 23

Validate XHTML 1.0