Simulações com o cMIPS

CI064, 2019-1 © Roberto André Hexsel, 2014-2019
Antes de mais nada, crie um prompt vazio, copiando a linha abaixo no seu terminal alias prompt:="" Três clicks no botão esquerdo para copiar, um click no meio para colar.
Sobre os diretórios de simulação O simulador deve ser executado no diretório ~/cmips/cMIPS Os testes devem ser executados no diretório ~/cmips/cMIPS/tests Os programas de teste devem ser compilados no diretório ~/cmips/cMIPS/tests e os "executáveis" prog.bin e data.bin devem ser movidos para ~/cmips/cMIPS Sobre a variável de ambiente PATH A variável PATH deve ser aumentada com o caminho dos scripts necessários para as simulações. Se, por acaso ou acidente, você fechar a Shell em que executava as simulações, o caminho para os scripts deve novamente ser acrescentado à PATH.

0- Faça a clonagem do repositório

Se você já tem uma cópia, atualize-a com: prompt: cd ~/cmips ; git pull Para copiar o código fonte do simulador para sua área, execute prompt: cd ; git clone https://github.com/rhexsel/cmips.git Mantenha o simulador instalado porque ele será necessário para o trabalho desta disciplina. Toda a vez em que uma nova versão for copiada do github, aqueles arquivos do repositório que foram atualizados (ou corrigidos) no repositório serão atualizados no seu clone. Para atualizar sua cópia do repositório diga: prompt: cd ~/cmips ; git pull ACHTUNG: Se você alterar arquivo(s) em sua cópia local, suas alterações poderão ser sobrescritas pela versão do repositório.

1- Teste sua cópia

Acrescente o diretório dos executáveis do cMIPS ao seu caminho: prompt: export PATH=$PATH:~/cmips/cMIPS/bin Construa o simulador: prompt: cd ~/cmips/cMIPS prompt: build.sh O script build.sh compila o código VHDL do simulador. Este script deve executar por uns 10 segundos, e pode produzir a saída de um diff, caso alguma configuração de endereço seja automagicamente efetuada. Em geral, o script só produz uma lista de arquivos VHDL compilados, a menos que ocorra algum erro. Neste caso, informe ao professor. O executável do simulador do cMIPS se chama tb_cmips. Execute os programas de teste para garantir que o simulador está correto: prompt: cd tests prompt: ./doTests.sh Uma lista de nomes é impressa na tela. Executar os testes toma de 1 a 3 minutos, dependendo da capacidade do seu computador. Se os testes não produziram nenhuma mensagem de erro, sua instalação está em ordem. Do contrário, informe ao professor.

2- Monte um arquivo em assembly

Ainda no diretório de testes, montemos um programa em assembly. Vejamos o código fonte de fwdSimple.s, que mostra os circuitos de adiantamento em ação. Os números das linhas não fazem parte do programa fonte.
1 .include "cMIPS.s" 2 .text 3 .align 2 4 .set noat 5 .globl _start 6 .ent _start 7 8 _start: la $15, x_IO_BASE_ADDR # stdout 9 addi $1, $0, 1 10 addi $5, $0, 5 11 12 nop # do not touch these 3 NOPS 13 nop 14 nop 15 16 add $6, $5, $1 # $5 + 1 17 nop # remove on first test 18 nop # remove on second test 19 nop # remove on third test 20 21 add $7, $6, $1 # $6 + 1 22 nop # remove on first test 23 nop # remove on second test 24 nop # remove on third test 25 26 add $8, $7, $1 # $7 + 1 27 nop # remove on first test 28 nop # remove on second test 29 nop # remove on third test 30 31 sw $6, 0($15) # print $6 32 sw $7, 0($15) # print $7 33 sw $8, 0($15) # print $8 34 35 nop # do not touch these 5 NOPS 36 nop # they are here to flush the pipeline 37 nop 38 nop 39 nop 40 wait # end simulation 41 nop 42 .end _start
O que há em cada linha do código fonte? 1: inclui arquivo com as definições dos endereços 2: início da seção com código (.text) 3: endereços devem ser alinhados em endereços múltiplos de 4=2**2 4: não usar o registrador $at como Assembly Temporary, pode usar $1 5: _start é um símbolo visível fora deste arquivo 6: ponto de entrada do código neste arquivo (primeira instrução por executar) 8: carrega em $15 o endereço do stdout do simulador, para imprimir na tela 31-33: escreve os valores de $6, $7, $8 na saída padrão do simulador 35-39: esvazia os segmentos do processador 40: a instrução wait termina a simulação 42: final do código de _start. Os cinco NOPs no final do programa servem para "esvaziar" o processador simulado, antes que esse decodifique a instrução wait. Essa instrução interrompe a simulação no instante em que é decodificada, e sem os NOPs, a simulação seria interrompida cedo demais, possivelmente produzindo resultados incompletos. Estes NOPs são um artefato da simulação e não do processador. Vejamos o resultado da montagem, mostrado e comentado abaixo. prompt: assemble.sh -v -z fwdSimple.s A opção -v (verbosa) mostra o resultado da montagem do programa; a opção -z (zeroes) desmonta e mostra os zeros no programa.
fwdSimple.elf: file format elf32-littlemips Disassembly of section .text: 00000000 <_start>: 0: 3c0f3c00 lui $15,0x3c00 4: 20010001 addi $1,$0,1 8: 20050005 addi $5,$0,5 c: 00000000 nop 10: 00000000 nop 14: 00000000 nop 18: 00a13020 add $6,$5,$1 1c: 00000000 nop 20: 00000000 nop 24: 00000000 nop 28: 00c13820 add $7,$6,$1 2c: 00000000 nop 30: 00000000 nop 34: 00000000 nop 38: 00e14020 add $8,$7,$1 3c: 00000000 nop 40: 00000000 nop 44: 00000000 nop 48: ade60000 sw $6,0($15) 4c: ade70000 sw $7,0($15) 50: ade80000 sw $8,0($15) 54: 00000000 nop 58: 00000000 nop 5c: 00000000 nop 60: 00000000 nop 64: 00000000 nop 68: 42000020 wait 6c: 00000000 nop
Este programa executa no processador sem nenhuma infraestrutura que não esteja no próprio código, e neste exemplo há somente uma seção .text, com as instruções do programa. O que é exibido é a desmontagem do resultado da montagem e ligação, que é o conteúdo do arquivo intermediário chamado fwdSimple.elf. As quatro colunas são, a partir da margem esquerda: endereço da instrução, a instrução, seu equivalente em assembly, e comentários (não há neste caso). O código inicia no endereço zero, e termina no endereço 0x6c. Os endereços e as instruções são codificados em hexadecimal e serão muito úteis no acompanhamento das simulações com o GTKWAVE. A montagem e a compilação para o cMIPS produzem os arquivos prog.bin e data.bin, com o código a ser armazenado na ROM, e os dados que são armazenados na RAM, caso seu programa contenha variáveis inicializadas. Para que este programa possa ser simulado, prog.bin e o data.bin devem ser copiados para o diretório acima de tests: prompt: assemble.sh -v fwdSimple.s && mv prog.bin data.bin .. && cd ..

3- Simulação sem visualização

De volta ao diretório cMIPS, executemos a simulação: prompt: run.sh Este script executa a simulação com base nos arquivos prog.bin e data.bin. No caso do fwdSimple.s a saída é:
00000006 00000007 00000008 core.vhd:851:7:@650ns:(assertion failure): cMIPS BREAKPOINT at PC=00000068 opc=010000 fun=100000 brk=10000000000000000000 SIMULATION ENDED (correctly?) AT exit(); /home/roberto/cMIPS/tb_cmips:error: assertion failed /home/roberto/cMIPS/tb_cmips:error: simulation failed
No endereço x_IO_BASE_ADDR fica o periférico print, e qualquer escrita neste endereço é enviada para a saída padrão do simulador e exibida em hexadecimal, com 8 dígitos. O programa fwdSimple.s escreve três inteiros na saída padrão e então encerra a simulação, ao executar a instrução wait. As cinco linhas após os números são produzidas pelo simulador, e não pela execução do programa fwdSimple.s. A primeira indica o tempo simulado de execução (em vermelho); a segunda e a terceira indicam o endereço e opcode da instrução que causou o término do programa, no wait do endereço 0x68 (em azul). O brk indica a condição que causou o término do programa, e neste caso a condição é zero (ignore o 1 à esquerda); a condição é mostrada nos 5 bits menos significativos (em magenta). As duas últimas linhas são geradas por GHDL e não sei como nos livrar delas.

4- Simulação com visualização

Faço uso do arquivo ~/.gtkwaverc para a configuração do gtkwave, que desabilita a tela de abertura e escolhe o tamanho das letras, permitindo a exibição de (quase) todos os sinais na tela. O conteúdo do meu ~/.gtkwaverc é mostrado abaixo. Talvez nem todos os corpos e tamanhos de letra estejam disponíveis no seu sistema. O tipo fixed existe em todos os sistemas que conheço.
initial_window_x -1 left_justify_sigs 1 enable_fast_exit 1 splash_disable 1 vcd_preserve_glitches 1 fontname_signals fixed 8 # fontname_waves terminus 7 fontname_waves fixed 7
Antes de invocar, é necessário escolher o arquivo com a configuração do gtkwave. Para invocar o gtkwave deve-se passar o argumento -w para run.sh: prompt: run.sh -w -v pipe_fwd.sav & pipe_fwd.sav é o arquivo de configuração do gtkwave e contém todos os sinais relevantes para este experimento. O argumento -v seleciona o arquivo de configuraçào do gtkwave. O programa é simulado e o gtkwave é então iniciado em background. Os sinais do cMIPS mostrados na tela são agrupados por estágio do pipeline, e de cima para baixo são Busca (fetch), Decodificação (decode) Execução (exec), Memória (memory), e Resultado (write-back). A figura abaixo mostra o diagrama de tempos com os agrupamentos de sinais indicados pelos retângulos coloridos. Os valores mostrados no painel Signals correspondem ao instante "embaixo" do cursor (linha vertical vermelha) que está exatamente no instante 253,2ns. O cursor cobre a execução de 5 instruções distintas. A instrução na caixa azul é o add do endereço 0x0000.0018: 18: 00a13820 add $6,$5,$1 e as caixas coloridas mostram a evolução desta instrução ao longo dos cinco estágios. No write-back, o resultado é gravado no registrador de destino (wb_a_c=6) porque o sinal wb_c=1 está ativo. A figura mostra, incorretamente, wb_a_c=5. diagrama de tempo No ciclo em 520ns ocorre um acesso ao periférico (não é mostrado na figura), que é a instrução sw no endereço 0x0000.0048. Endereços na faixa de 0x00XX.0000 a 0x00XX.1ffff (X != 0) são alocados à RAM, enquanto que endereços na faixa 0x3c00XXXX são alocados aos periféricos. O arquivo docs/cMIPS.pdf contém diagramas com os nomes de (quase) todos os sinais do processador, de forma que é possível acompanhar a execução das instruções pelos sinais mostrados no diagrama de tempo. Agora a parte trabalhosa: uma instrução é mostrada no diagrama de tempo como se estivesse descendo uma escada. No ciclo C, ela está na busca; em C+1 na decodificação e desceu um degrau; no ciclo C+2 na execução e um degrau abaixo; no ciclo C+3 na memória, e no ciclo C+4 no write-back que é o último degrau. Olhando o diagrama durante um ciclo que é uma fatia vertical do diagrama de tempos, o que vemos são cinco instruções distintas, uma em cada fase de execução. Olhando o diagrama ao longo de uma fatia horizontal, o que vemos são várias instruções num mesmo estágio. Para seguir a execução de uma instrução, devemos seguir uma fatia diagonal do diagrama. Os retângulos coloridos mostram os cinco estágios da execução do add do endereço 0x0000.0018. Descubra os valores dos sinais relevantes para esta instrução nos diagramas do processador em docs/cMIPS.pdf. Siga pois a execução do programa fwdSimple, acompanhando a execução com a saída do montador: observe o endereço da instrução (no PC) e confirme seu opcode no estágio de decodificação; depois siga a execução até o estágio write-back. Altere o programa três vezes: (i) na primeira vez, remova os três NOPs indicados para o primeiro teste, monte, simule novamente e verifique a corretude do resultado e os sinais no diagrama de tempo; (ii) repita, removento os três NOPS do segundo teste; e (iii) repita, removendo os três NOPs do terceiro teste. O circuito de adiantamento opera corretamente?

5- Compile um programa escrito em C

Volte para o diretório tests e compile o programa fat.c: prompt: cd tests prompt: compile.sh -v fat.c A saída é algo maior do que a saída do montador e o código desmontado contém a inicialização de um proto-SO e tratadores simples de excessões. O código assembly da inicialização do proto-SO está em include/start.s e dos tratadores em include/handlers.s.
fat.elf: file format elf32-littlemips Disassembly of section .text: 00000000 <_start>: # reset faz PC=0000.0000 0: 241a0004 li $26,4 4: 409a0000 mtc0 $26,$0 8: 000000c0 ehb c: 42000001 tlbr 10: 241b0002 li $27,2 14: 409b0000 mtc0 $27,$0 18: 000000c0 ehb 1c: 42000002 tlbwi 20: 24040018 li $4,24 24: 00043300 sll $6,$4,0xc 28: 40865000 mtc0 $6,$10 2c: 24040018 li $4,24 30: 00042980 sll $5,$4,0x6 34: 34a50007 ori $5,$5,0x7 38: 40851000 mtc0 $5,$2 3c: 24040019 li $4,25 40: 00042980 sll $5,$4,0x6 44: 34a50007 ori $5,$5,0x7 48: 40851800 mtc0 $5,$3 4c: 241a0004 li $26,4 50: 409a0000 mtc0 $26,$0 54: 42000002 tlbwi 58: 24040016 li $4,22 5c: 00043300 sll $6,$4,0xc 60: 40865000 mtc0 $6,$10 64: 24040016 li $4,22 68: 00042980 sll $5,$4,0x6 6c: 34a50007 ori $5,$5,0x7 70: 40851000 mtc0 $5,$2 74: 24040017 li $4,23 78: 00042980 sll $5,$4,0x6 7c: 34a50007 ori $5,$5,0x7 80: 40851800 mtc0 $5,$3 84: 241a0003 li $26,3 88: 409a0000 mtc0 $26,$0 8c: 42000002 tlbwi 90: 241a0004 li $26,4 94: 409a3000 mtc0 $26,$6 98: 3c1d0001 lui $29,0x1 9c: 37bd7ff0 ori $29,$29,0x7ff0 a0: 3c1a1000 lui $26,0x1000 a4: 375ae011 ori $26,$26,0xe011 a8: 409a6000 mtc0 $26,$12 ac: 0c00026d jal 9b4 <main> # salta para main() b0: 00000000 nop 000000b4 <_exit>: # simulação termina aqui b4: 00000000 nop b8: 00000000 nop bc: 00000000 nop c0: 00000000 nop c4: 00000000 nop c8: 42000020 wait cc: 00000000 nop # vários NOPs removidos 00000130 <_excp_0000>: 130: 401b2000 mfc0 $27,$4 134: 8f7a0000 lw $26,0($27) 138: 8f7b0008 lw $27,8($27) 13c: 409a1000 mtc0 $26,$2 140: 409b1800 mtc0 $27,$3 144: 000000c0 ehb 148: 42000006 tlbwr 14c: 42000018 eret 150: 00000000 nop 154: 00000000 nop # vários NOPs removidos 00000200 <_excp_0100>: 200: 3c1a0f00 lui $26,0xf00 204: 401b6800 mfc0 $27,$13 208: af5b0000 sw $27,0($26) 20c: 00000000 nop 210: 00000000 nop 214: 00000000 nop 218: 42000060 wait 0x1 21c: 00000000 nop 220: 00000000 nop 224: 00000000 nop # vários NOPs removidos 00000280 <_excp_0180>: 280: 401a6000 mfc0 $26,$12 284: 3c1b0001 lui $27,0x1 288: 377b0040 ori $27,$27,0x40 28c: af7a0004 sw $26,4($27) 290: 401a6800 mfc0 $26,$13 294: af7a0000 sw $26,0($27) 298: 335a003f andi $26,$26,0x3f 29c: 001ad040 sll $26,$26,0x1 2a0: 3c1b0000 lui $27,0x0 2a4: 377b02b4 ori $27,$27,0x2b4 2a8: 037ad820 add $27,$27,$26 2ac: 03600008 jr $27 2b0: 00000000 nop 000002b4 <excp_tbl>: 2b4: 420000a0 wait 0x2 2b8: 00000000 nop 2bc: 080000cd j 334 <excp_0180ret> 2c0: 00000000 nop 2c4: 080000cd j 334 <excp_0180ret> 2c8: 00000000 nop 2cc: 080000cd j 334 <excp_0180ret> 2d0: 00000000 nop 2d4: 42000120 wait 0x4 2d8: 00000000 nop 2dc: 42000160 wait 0x5 2e0: 00000000 nop 2e4: 420001a0 wait 0x6 2e8: 00000000 nop 2ec: 420001e0 wait 0x7 2f0: 00000000 nop 2f4: 080000cd j 334 <excp_0180ret> 2f8: 00000000 nop 2fc: 080000cd j 334 <excp_0180ret> 300: 00000000 nop 304: 080000cd j 334 <excp_0180ret> 308: 00000000 nop 30c: 080000cd j 334 <excp_0180ret> 310: 00000000 nop 314: 080000cd j 334 <excp_0180ret> 318: 00000000 nop 31c: 42000360 wait 0xd 320: 00000000 nop 324: 420003a0 wait 0xe 328: 00000000 nop 32c: 420003e0 wait 0xf 330: 00000000 nop 00000334 <excp_0180ret>: 334: 3c1b0001 lui $27,0x1 338: 377b0040 ori $27,$27,0x40 33c: 8f7a0004 lw $26,4($27) 340: 3c1bffff lui $27,0xffff 344: 377bfff1 ori $27,$27,0xfff1 348: 375aff01 ori $26,$26,0xff01 34c: 037ad024 and $26,$27,$26 350: 409a6000 mtc0 $26,$12 354: 42000018 eret 358: 00000000 nop 35c: 00000000 nop # vários NOPs removidos 00000400 <_excp_0200>: 400: 401a6800 mfc0 $26,$13 404: 335aff00 andi $26,$26,0xff00 408: 401b6000 mfc0 $27,$12 40c: 035bd024 and $26,$26,$27 410: 001ad2c2 srl $26,$26,0xb 414: 3c1b0000 lui $27,0x0 418: 377b0428 ori $27,$27,0x428 41c: 037ad820 add $27,$27,$26 420: 03600008 jr $27 424: 00000000 nop 00000428 <handlers_tbl>: 428: 0800011a j 468 <Dismiss> 42c: 00000000 nop 430: 08000140 j 500 <extCounter> 434: 00000000 nop 438: 08000152 j 548 <UARTinterr> 43c: 00000000 nop 440: 08000152 j 548 <UARTinterr> 444: 00000000 nop 448: 0800016b j 5ac <countCompare> 44c: 00000000 nop 450: 0800016b j 5ac <countCompare> 454: 00000000 nop 458: 0800016b j 5ac <countCompare> 45c: 00000000 nop 460: 0800016b j 5ac <countCompare> 464: 00000000 nop 00000468 <Dismiss>: 468: 401a6000 mfc0 $26,$12 46c: 201bfff1 addi $27,$0,-15 470: 375aff01 ori $26,$26,0xff01 474: 037ad024 and $26,$27,$26 478: 409a6000 mtc0 $26,$12 47c: 42000018 eret 480: 00000000 nop 484: 00000000 nop # vários NOPs removidos 000004e0 <_excp_BFC0>: 4e0: 3c1a0f00 lui $26,0xf00 4e4: 401b6800 mfc0 $27,$13 4e8: af5b0000 sw $27,0($26) 4ec: 00000000 nop 4f0: 00000000 nop 4f4: 00000000 nop 4f8: 42003fe0 wait 0xff 4fc: 00000000 nop 00000500 <extCounter>: 500: 3c1a0f00 lui $26,0xf00 504: 375a00a0 ori $26,$26,0xa0 508: af400000 sw $0,0($26) 50c: 3c1bc000 lui $27,0xc000 510: 377b00c8 ori $27,$27,0xc8 514: af5b0000 sw $27,0($26) 518: 3c1a0001 lui $26,0x1 51c: 375a002c ori $26,$26,0x2c 520: 8f5b0000 lw $27,0($26) 524: 00000000 nop 528: 277b0001 addiu $27,$27,1 52c: af5b0000 sw $27,0($26) 530: 401a6000 mfc0 $26,$12 534: 375aff09 ori $26,$26,0xff09 538: 241bfff9 li $27,-7 53c: 037ad024 and $26,$27,$26 540: 409a6000 mtc0 $26,$12 544: 42000018 eret 00000548 <UARTinterr>: 548: 3c1a0f00 lui $26,0xf00 54c: 375a00e0 ori $26,$26,0xe0 550: 8f5b0000 lw $27,0($26) 554: 3c1a0001 lui $26,0x1 558: 375a00c0 ori $26,$26,0xc0 55c: af5b0000 sw $27,0($26) 560: af44000c sw $4,12($26) 564: af450010 sw $5,16($26) 568: 33640008 andi $4,$27,0x8 56c: 10800007 beqz $4,58c <UARTret> 570: 3c040f00 lui $4,0xf00 574: 348400e0 ori $4,$4,0xe0 578: 8c850004 lw $5,4($4) 57c: 00000000 nop 580: af450004 sw $5,4($26) 584: 24050001 li $5,1 588: af450008 sw $5,8($26) 0000058c <UARTret>: 58c: 8f450010 lw $5,16($26) 590: 8f44000c lw $4,12($26) 594: 401a6000 mfc0 $26,$12 598: 375aff09 ori $26,$26,0xff09 59c: 241bfff9 li $27,-7 5a0: 037ad024 and $26,$27,$26 5a4: 409a6000 mtc0 $26,$12 5a8: 42000018 eret 000005ac <countCompare>: 5ac: 401b4800 mfc0 $27,$9 5b0: 277b0040 addiu $27,$27,64 5b4: 409b5800 mtc0 $27,$11 5b8: 401a6000 mfc0 $26,$12 5bc: 375aff09 ori $26,$26,0xff09 5c0: 3c1bffff lui $27,0xffff 5c4: 377bfff9 ori $27,$27,0xfff9 5c8: 037ad024 and $26,$27,$26 5cc: 409a6000 mtc0 $26,$12 5d0: 42000018 eret 000005d4 <enableInterr>: 5d4: 40026000 mfc0 $2,$12 5d8: 34420001 ori $2,$2,0x1 5dc: 40826000 mtc0 $2,$12 5e0: 00000000 nop 5e4: 03e00008 jr $31 5e8: 00000000 nop 000005ec <disableInterr>: 5ec: 40026000 mfc0 $2,$12 5f0: 2403fffe li $3,-2 5f4: 00431024 and $2,$2,$3 5f8: 40826000 mtc0 $2,$12 5fc: 00000000 nop 600: 03e00008 jr $31 604: 00000000 nop 00000608 <cmips_delay>: 608: 2484ffff addiu $4,$4,-1 60c: 00000000 nop 610: 1480fffd bnez $4,608 <cmips_delay> 614: 00000000 nop 618: 03e00008 jr $31 61c: 00000000 nop 00000620 <from_stdin>: 620: 3c020f00 lui $2,0xf00 624: 8c420040 lw $2,64($2) 628: 03e00008 jr $31 62c: 00000000 nop 00000630 <to_stdout>: 630: 308400ff andi $4,$4,0xff 634: 3c020f00 lui $2,0xf00 638: 03e00008 jr $31 63c: ac440020 sw $4,32($2) 00000640 <print>: 640: 3c020f00 lui $2,0xf00 644: 03e00008 jr $31 648: ac440000 sw $4,0($2) # várias funções de biblioteca removidas # código de fat.c inicia aqui 00000908 <myprint>: 908: 27bdffe0 addiu $29,$29,-32 90c: afbf001c sw $31,28($29) 910: afb10018 sw $17,24($29) 914: afb00014 sw $16,20($29) 918: 00808821 move $17,$4 91c: 0c000190 jal 640 <print> 920: 00a08021 move $16,$5 924: 0c000190 jal 640 <print> 928: 02002021 move $4,$16 92c: 02301021 addu $2,$17,$16 930: 8fbf001c lw $31,28($29) 934: 8fb10018 lw $17,24($29) 938: 8fb00014 lw $16,20($29) 93c: 03e00008 jr $31 940: 27bd0020 addiu $29,$29,32 00000944 <fat>: 944: 1080000b beqz $4,974 <fat+0x30> 948: 00000000 nop 94c: 27bdffe8 addiu $29,$29,-24 950: afbf0014 sw $31,20($29) 954: afb00010 sw $16,16($29) 958: 00808021 move $16,$4 95c: 0c000251 jal 944 <fat> 960: 2484ffff addiu $4,$4,-1 964: 02020018 mult $16,$2 968: 00001012 mflo $2 96c: 10000003 b 97c <fat+0x38> 970: 00000000 nop 974: 03e00008 jr $31 978: 24020001 li $2,1 97c: 8fbf0014 lw $31,20($29) 980: 8fb00010 lw $16,16($29) 984: 03e00008 jr $31 988: 27bd0018 addiu $29,$29,24 0000098c <fat2>: 98c: 2483ffff addiu $3,$4,-1 990: 18600006 blez $3,9ac <fat2+0x20> 994: 00801021 move $2,$4 998: 00430018 mult $2,$3 99c: 00001012 mflo $2 9a0: 2463ffff addiu $3,$3,-1 9a4: 1460fffd bnez $3,99c <fat2+0x10> 9a8: 00430018 mult $2,$3 9ac: 03e00008 jr $31 9b0: 00000000 nop 000009b4 <main>: 9b4: 27bdffe8 addiu $29,$29,-24 9b8: afbf0014 sw $31,20($29) 9bc: 0c000263 jal 98c <fat2> 9c0: 24040007 li $4,7 9c4: 00402821 move $5,$2 9c8: 0c000242 jal 908 <myprint> 9cc: 24040007 li $4,7 9d0: 0c000251 jal 944 <fat> 9d4: 24040007 li $4,7 9d8: 00402821 move $5,$2 9dc: 0c000242 jal 908 <myprint> 9e0: 24040007 li $4,7 9e4: 0c000263 jal 98c <fat2> 9e8: 2404000c li $4,12 9ec: 00402821 move $5,$2 9f0: 0c000242 jal 908 <myprint> 9f4: 2404000c li $4,12 9f8: 0c000251 jal 944 <fat> 9fc: 2404000c li $4,12 a00: 00402821 move $5,$2 a04: 0c000242 jal 908 <myprint> a08: 2404000c li $4,12 a0c: 8fbf0014 lw $31,20($29) a10: 00000000 nop a14: 03e00008 jr $31 a18: 27bd0018 addiu $29,$29,24 # área de dados Disassembly of section .sbss: 00010018 <nrx>: 10018: 00000000 nop 0001001c <ntx>: 1001c: 00000000 nop 00010020 <tx_tl>: 10020: 00000000 nop 00010024 <rx_tl>: 10024: 00000000 nop 00010028 <tx_hd>: 10028: 00000000 nop 0001002c <_counter_val>: 1002c: 00000000 nop 00010030 <rx_hd>: 10030: 00000000 nop Disassembly of section .bss: 00010040 <Ud>: 10040: 00000000 nop 10044: 00000000 nop 10048: 00000000 nop 1004c: 00000000 nop 10050: 00000000 nop 10054: 00000000 nop 10058: 00000000 nop 1005c: 00000000 nop 10060: 00000000 nop 10064: 00000000 nop 10068: 00000000 nop 1006c: 00000000 nop 10070: 00000000 nop 10074: 00000000 nop 10078: 00000000 nop 1007c: 00000000 nop 00010080 <rx_queue>: 10080: 00000000 nop 10084: 00000000 nop 10088: 00000000 nop 1008c: 00000000 nop 00010090 <_counter_saves>: 10090: 00000000 nop 10094: 00000000 nop 10098: 00000000 nop 1009c: 00000000 nop 100a0: 00000000 nop 100a4: 00000000 nop 100a8: 00000000 nop 100ac: 00000000 nop 000100b0 <tx_queue>: 100b0: 00000000 nop 100b4: 00000000 nop 100b8: 00000000 nop 100bc: 00000000 nop 000100c0 <_uart_buff>: 100c0: 00000000 nop 100c4: 00000000 nop 100c8: 00000000 nop 100cc: 00000000 nop 100d0: 00000000 nop 100d4: 00000000 nop 100d8: 00000000 nop 100dc: 00000000 nop 100e0: 00000000 nop 100e4: 00000000 nop 100e8: 00000000 nop 100ec: 00000000 nop 100f0: 00000000 nop 100f4: 00000000 nop 100f8: 00000000 nop 100fc: 00000000 nop # vários NOPs e variáveis removidas
As variáveis alocadas nas seções .sbss e .bss (Block Started by Symbol) contém as variáveis para o tratador (driver) da interface serial, que será objeto do trabalho desta disciplina. Voltaremos nossa atenção a elas nas próximas aulas. O programa fat.c só usa a pilha, e não aloca nenhuma variável estática. ACHTUNG: na descrição que segue, os endereços das instruções e das variáveis são aqueles da listagem acima. Quando compilar seu programa, muito provavelmente os endereços serão outros, seja por causa do seu código, seja por causa das alterações introduzidas nos "arquivos de sistema". As instruções do endereço 0x0000.0000 até 0x0000.061c estão nos arquivos include/start.s e include/handlers.s, que contém as rotinas de tratamento de interrupções e excessões, que estudaremos em breve. Do endereço 0x0000.0620 até 0x0000.0904 estão as instruções que resultam da compilação do arquivo include/cMIPSio.c. O código das "funções de biblioteca" do cMIPS faz a interface com os "periféricos" de apresentação de resultados, leitura e escrita de arquivos, e de acesso aos periféricos propriamente ditos. As instruções do programa fat.c vão de 0x0000.0908 a 0x0000.0a18, com com myprint(), seguido de fat(), fat2(), e main(). Como no caso do montador, copie prog.bin e data.bin para o diretório acima, mude para lá, e execute a simulação do fatorial (run.sh). O resultado é mostrado adiante, com os fatoriais de 7 e 12 computados pela funções recursiva e interativa. prompt: mv prog.bin data.bin .. ; cd .. ; run.sh
00000007 # fatorial de 7 000013b0 00000007 # fatorial de 7 000013b0 0000000c # fatorial de 12 1c8cfc00 0000000c # fatorial de 12 1c8cfc00 core.vhd:775:7:@11835ns:(assertion failure): cMIPS BREAKPOINT at PC=000000c8 opc=010000 fun=100000 brk=10000000000000000000 SIMULATION ENDED (correctly?) AT exit(); /home/roberto/cMIPS/tb_cmips:error: assertion failed /home/roberto/cMIPS/tb_cmips:error: simulation failed
Seguir a execução de programas escritos em C pode ser um tanto mais cansativo do que programas em assembly por causa de seus respectivos tamanhos. Contudo, esta é a forma mais detalhada de depuração de um programa que eu consigo imaginar... Todos os bits associados à execução de um programa estão disponíveis para exame, e neste caso, o "todos" pode ser um tanto a mais do que o desejado. Para seguir a execução com gtkwave com um mínimo de sinais, diga: prompt: run.sh -w -v pipe_min.sav & Para seguir a execução com gtkwave com um tanto de sinais, diga: prompt: run.sh -w -v pipe_Med.sav & Para seguir a execução com gtkwave com um montão de sinais, diga: prompt: run.sh -w -v pipe_MAX.sav &

6- Da compilação

O executável do fatorial é obtido a partir da compilação e ligação de quatro arquivos com código fonte:
  1. start.s contém o código que inicializa o processador e mais o código que despacha o tratamento das excessões e interrupções. Fonte em cMIPS/include/start.s;
  2. handlers.s contém o código dos tratadores das interrupções do contador externo, da UART (interface serial), e do contador interno. Em breve estudaremos os periféricos e interrupções. Fonte em cMIPS/include/handlers.s;
  3. cMIPSio.c contém o código para acesso aos "periféricos" que são usados nas simulações, que permitem ler da entrada padrão do simulador, escrever na saída padrão do simulador, ler ou gravar arquivos. Fonte em cMIPS/include/cMIPSio.c;
  4. fat.c contém o código do fatorial. Fonte em cMIPS/tests/fat.c.
Estes arquivos são compilados individualmente e então ligados, gerando o "simulável" (ao invés de 'executável') fat.elf. Este arquivo é quebrado em dois, prog.bin com o binário que é interpretado pelo cMIPS, e data.bin com dados inicializados. O script cMIPS/bin/compile.sh efetua todos os passos necessários para gerar os arquivos "simuláveis". Uma versão extremamente simplificada da receita de compilação é mostrada abaixo. Todos os defaults do script já estão substituídos, e muitos detalhes são omitidos. A opção -EL é para gerar código em modo little-endian. # compila os fontes em C para assembly mips-gcc -S -O1 -DcMIPS fat.c -o fat.s mips-gcc -S -O1 -DcMIPS include/cMIPSio.c -o cMIPSio.s # monta todos os assembly para arquivos objeto mips-as -O1 -EL -mips32 fat.s -o fat.o mips-as -O1 -EL -mips32 include/start.s -o start.o mips-as -O1 -EL -mips32 include/handlers.s -o handlers.o mips-as -O1 -EL -mips32 cMIPSio.s -o cMIPSio.o # liga todos os objeto e produz fat.elf, com o mapa de memória do cMIPS mips-ld -EL -e _start --script include/cMIPS.ld \ start.o handlers.o cMIPSio.o fat.o -o fat.elf # separa instruções de dados de fat.elf para prog.bin e data.bin mips-objcopy -S -j .text -O binary fat.elf prog.bin mips-objcopy -S -j .data -j .rodata -j .rodata1 -j .data1 \ -j .sdata -j .lit8 -j .lit4 -j .sbss -j .bss \ -O binary fat.elf data.bin # se é para ser verboso, então desmonte o fat.elf mips-objdump -z -D -EL -M reg-names=mips2r2 -M cp0-names=mips2r2 \ --show-raw-insn --section .text --section .data \ --section .rodata --section .sdata --section .sbss \ --section .bss fat.elf Para acompanhar a compilação em toda a sua glória, diga prompt: compile.sh -x fat.c

7- Documentação

O arquivo docs/cMIPS.pdf contém toda a documentação do cMIPS. Para além disso, veja o código fonte. --fim da aula-- Para ler no fim de semana: My recollections of operating system design.