Hardware/Software co-design para aplicações de processamento de voz



Documentos relacionados
Capítulo 4. MARIE (Machine Architecture Really Intuitive and Easy)

Interrupções. As interrupções são casos especiais de chamadas de procedimentos.

BARRAMENTO DO SISTEMA

Entrada e Saída. Interface entre periféricos, processador e memória. Fonte: Minho - Portugal 1

O processador é composto por: Unidade de controlo - Interpreta as instruções armazenadas; - Dá comandos a todos os elementos do sistema.

Figura 1 - O computador

O hardware é a parte física do computador, como o processador, memória, placamãe, entre outras. Figura 2.1 Sistema Computacional Hardware

Um sistema SMS 1 simplificado

Sistemas Operativos I

Introdução à Arquitetura de Computadores

Conjunto de instruções do CPU. Arquitectura de um computador. Definição das instruções (1) Definição das instruções (2)

Edeyson Andrade Gomes

Introdução aos Computadores

Unidade Central de Processamento (CPU) Processador. Renan Manola Introdução ao Computador 2010/01

Tais operações podem utilizar um (operações unárias) ou dois (operações binárias) valores.

ICORLI. INSTALAÇÃO, CONFIGURAÇÃO e OPERAÇÃO EM REDES LOCAIS e INTERNET

Figura 3.1 Diagrama de blocos do codificador de voz do ITU-T G.723.1

Sistemas Operativos. Sumário. Estruturas de sistemas de computação. ! Operação de um sistema de computação. ! Estruturas de E/S

ARQUITETURA DE COMPUTADORES

Arquitetura de Computadores. Tipos de Instruções

Sistemas Operacionais. Prof. André Y. Kusumoto

CAPÍTULO 7 NÍVEL DE LINGUAGEM DE MONTAGEM

MÓDULO 7 Modelo OSI. 7.1 Serviços Versus Protocolos

Organização e Arquitetura de Computadores

CAPÍTULO 2 CARACTERÍSTICAS DE E/S E PORTA PARALELA

Memória. Espaço de endereçamento de um programa Endereços reais e virtuais Recolocação dinâmica Segmentação

SISTEMAS DIGITAIS. Memórias. Prof. Guilherme Arroz Prof. Carlos Sêrro Alterado para lógica positiva por Guilherme Arroz.

Escola Secundária de Emídio Navarro

Programação de Sistemas

Componentes do Computador e. aula 3. Profa. Débora Matos

Capacidade = 512 x 300 x x 2 x 5 = ,72 GB

Codificadores de voz do MPEG-4. Eriko Porto

3/9/2010. Ligação da UCP com o barramento do. sistema. As funções básicas dos registradores nos permitem classificá-los em duas categorias:

ULA Sinais de Controle enviados pela UC

MEMÓRIA. 0 e 1 únicos elementos do sistema de numeração de base 2

Primeiros "computadores" digitais. Execução de um programa. Consolas. Primórdios dos computadores. Memória interna. Computadores com memória interna

3. Arquitetura Básica do Computador

SISTEMAS INFORMÁTICOS

Capítulo 3. Avaliação de Desempenho. 3.1 Definição de Desempenho

Arquitetura de Computadores Paralelismo, CISC X RISC, Interpretação X Tradução, Caminho de dados

ARQUITETURA DE COMPUTADORES

Sistemas Computacionais II Professor Frederico Sauer

Arquiteturas RISC. (Reduced Instructions Set Computers)

Quadro de consulta (solicitação do mestre)

Organização de Computadores 1

Microprocessadores. Prof. Leonardo Barreto Campos 1

Programação Básica em STEP 7 Operações Binárias. SITRAIN Training for Automation and Drives. Página 6-1

28/9/2010. Unidade de Controle Funcionamento e Implementação

Sistemas Operativos I

Mecanismo de Interrupção

FACULDADE PITÁGORAS DISCIPLINA: ARQUITETURA DE COMPUTADORES

Utilização do SOLVER do EXCEL

20/09/2009 TRANSFORMANDO DADOS EM. PROCESSANDO DADOS George Gomes Cabral SISTEMAS NUMÉRICOS INFORMAÇÕES

Prof. Sandrina Correia

Sistemas Operacionais

ENTRADA E SAÍDA DE DADOS

Introdução aos Sistemas Operativos

1 Transmissão digital em banda base

Componentes básicos de um sistema computacional. Cap. 1 (Stallings)

Ao longo do presente capítulo será apresentada uma descrição introdutória da tecnologia FPGA e dos módulos básicos que a constitui.

ENHANCED SERVER FAULT- TOLERANCE FOR IMPROVED USER EXPERIENCE. André Esteves nº3412 David Monteiro

CDE4000 MANUAL 1. INTRODUÇÃO 2. SOFTWARE DE CONFIGURAÇÃO 3. COMUNICAÇÃO

Hardware (Nível 0) Organização. Interface de Máquina (IM) Interface Interna de Microprogramação (IIMP)

TIC Unidade 2 Base de Dados. Informação é todo o conjunto de dados devidamente ordenados e organizados de forma a terem significado.

O protocolo MODBUS define também o tipo diálogo entre os equipamentos, define por exemplo quem pode enviar dados e em que altura.

Gestor de Processos Núcleo do Sistema Operativo. Sistemas Operativos 2011 / Gestor de Processos

NOTAS DE AULA Prof. Antonio Carlos Schneider Beck Filho (UFSM) Prof. Júlio Carlos Balzano de Mattos (UFPel) Arquitetura de Von Neumann

Arquitecturas Alternativas. Pipelining Super-escalar VLIW IA-64

A memória é um recurso fundamental e de extrema importância para a operação de qualquer Sistema Computacional; A memória trata-se de uma grande

Memória Cache. Prof. Leonardo Barreto Campos 1

Escola Secundária de Emídio Navarro

PHC Serviços CS. A gestão de processos de prestação de serviços

TECNOLOGIAS DA INFORMAÇÃO E COMUNICAÇÃO

5. EXPERIÊNCIAS E ANÁLISE DOS RESULTADOS Os Programas de Avaliação

UNIVERSIDADE FEDERAL DE SANTA CATARINA MODELAGEM DE UMA PLATAFORMA VIRTUAL PARA SISTEMAS EMBUTIDOS BASEADA EM POWERPC

2- Conceitos Básicos de Telecomunicações

Manual do utilizador Ethernet

Gerência de Memória. Paginação

Introdução a Informática. Prof.: Roberto Franciscatto

Entrada e Saída. Prof. Leonardo Barreto Campos 1

PROJETO DE REDES

UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL INSTITUTO DE INFORMÁTICA INFORMÁTICA APLICADA

Campus Capivari Análise e Desenvolvimento de Sistemas (ADS) Prof. André Luís Belini prof.andre.luis.belini@gmail.com /

Oficina de Multimédia B. ESEQ 12º i 2009/2010

Vitor Amadeu Souza.

1.3. Componentes dum sistema informático HARDWARE SOFTWARE

Estrutura de um Computador

Capítulo 8. Software de Sistema

Evolução dos Processadores

Introdução à Organização e Arquitetura de Computadores. Prof. Leonardo Barreto Campos 1

Arquitetura de Computadores I

DEPARTAMENTO DE ENGENHARIA INFORMÁTICA FACULDADE DE CIÊNCIAS E TECNOLOGIA DA UNIVERSIDADE DE COIMBRA

FAÇA FÁCIL: DRIVER IGS PARA COMUNICAÇÃO DE PROTOCOLOS PROPRIETÁRIOS INTRODUÇÃO

5 Entrada e Saída de Dados:

Módulo 8 Ethernet Switching

Arquitetura de Rede de Computadores

Fabio Bento

Transcrição:

Hardware/Software co-design para aplicações de processamento de voz Pedro Manuel Fonseca da Mota (ee00022) Pedro Manuel Vieira dos Santos (ee00115)

Hardware/Software co-design para aplicações de processamento de voz - 1 Este projecto foi desenvolvido no âmbito da cadeira de Projecto, Seminário ou Trabalho de fim de curso em parceria com a empresa Chipidea. Pretende-se agradecer a orientação dos responsáveis da Feup, professor João canas Ferreira e professor Aníbal José Ferreira e da empresa Chipidea, em particular aos Engº António Pacheco e Engº Vasco Santos, pela sua preciosa colaboração.

Hardware/Software co-design para aplicações de processamento de voz - 2 1 Sumário... 9 2 Introdução... 10 3 O codec de voz AMR... 12 3.1 Descrição geral...12 3.2 Princípios do codificador de voz AMR...14 3.3 Princípios do descodificador de voz AMR...18 3.4 Detector de presença de voz (VAD)...19 3.5 Controlo do débito (SCR)...21 3.6 Código C para o codec de voz AMR...21 3.6.1 Conteúdos do código C...22 3.6.2 Execução do programa...22 3.6.3 Estrutura do código...23 3.6.4 Hierarquia do código...27 4 OpenRISC 1200 RISC/DSP Core... 31 4.1 Descrição do OpenRISC 1200 Core...31 4.1.1 CPU/DSP...32 4.1.1.1 Unidade de Instruções...33 4.1.1.2 Registos para uso genérico (GPR)...33 4.1.1.3 Unidade de Load/Store...34 4.1.1.4 Excepções...34 4.1.1.5 System Unit...34 4.1.1.6 Pipeline e MAC Unit...34 4.1.1.7 DSP MAC...35 4.1.2 Caches...35 4.1.3 Memory Management Unit...36 4.1.4 Power Management Unit...36 4.1.5 Unidade de Debug...37 4.1.6 Tick Timer Integrado...37 4.1.7 Programmable Interrupt Controller...37 4.1.8 Unidades Adicionais E Definidas Pelo Utilizador...37 4.1.9 Ferramentas De Suporte Ao Desenvolvimento...38 4.1.10 Sistemas Operativos Suportados:...38 4.1.11 Interface do Sistema...38 4.2 Nível de transferência lógica de registos do OR1200...38 4.2.1 CPU Core top-level...41 4.2.2 Slices associadas ao CPU Core...44 4.2.3 Top-level do OPENRISC 1200...44 4.2.4 Adaptando o Or1k ao espaço disponível...47 4.3 Ferramentas de desenvolvimento...50 4.3.1 Ferramentas disponíveis e funcionalidade...50 4.3.2 Instalação das ferramentas...50 4.3.3 Produção de binários e ferramentas auxiliares...53 4.3.4 Simulação dos binários produzidos...61 4.3.5 Funções de interesse presentes na libraria usada no linking...68 5 Profiling e análise do codec AMR... 70 5.1 Introdução...70

Hardware/Software co-design para aplicações de processamento de voz - 3 5.2 Análise ao estilo de programação definido pela norma...70 5.2.1 Operações básicas...71 5.3 Uso do ficheiro count.c para realizar o profiling do codec...72 5.4 Profiling do codec AMR...73 5.5 Optimização do algoritmo pesquisa do codebook inovativo no modo 12.2 kbit/s.75 5.6 Medição das operações básicas no codec...78 6 Pequeno programa a simular no processador lei A... 80 6.1 Introdução...80 6.2 A lei A...80 6.3 Implementação da lei A no processador...81 7 O caminho para a implementação do Or1k em hardware... 83 7.1 Plataforma de desenvolvimento...83 7.2 Comunicação entre a memória e o Or1k...83 7.3 Controlador de Memórias...84 7.3.1 Área ocupada pelo controlador de memórias...86 7.3.2 Configuração do controlador de memórias...87 7.3.3 Aceder aos registos de configuração e ao espaço de memória...89 7.3.4 Testbench do controlador de memórias...91 7.3.5 Descrição das unidades auxiliares para teste...92 7.3.5.1 Testes pretendidos para o modelo desenvolvido e concretização...96 7.3.5.2 Formas de onda obtidas e o protocolo wishbone...97 7.4 Or1k e memory controller: necessidade de um arbitrador...102 7.4.1 Testbench para o sistema master/slave...105 8 Adaptação da norma do codec AMR à arquitectura do processador OpenRISC 1200 107 8.1 Definição dos tipos de variáveis no OpenRISC1200...107 8.2 Funções não reconhecidas pelo compilador do OpenRISC1200...108 8.3 Optimização das operações básicas no OpenRISC1200...111 8.3.1 Escrita de instruções assembly em código C...111 8.3.2 Colocação das operações básicas inline...112 8.3.3 O uso de macros mas operações básicas...113 8.3.4 Alteração do código nas funções básicas...115 8.3.4.1 As flags de Overflow e Carry...115 8.3.4.2 Instruções de adição/subtracção...116 8.3.4.3 Instruções de multiplicação...117 8.3.4.4 Instruções de normalização...118 8.3.4.5 Instruções shift left/right...118 8.3.4.6 Instruções mac...119 8.3.4.7 Outras instruções...121 8.3.5 Problemas encontrados...121 8.4 Problemas do compilador...124 8.5 Melhorias observadas pelo uso das macros...125 9 Perspectivas de desenvolvimento futuras... 127 9.1 Operações básicas a serem implementadas na arquitectura do OPENRISC1200...127 9.2 32 bits versus 16 bits...127 10 Conclusões... 129 11 Bibliografia... 131

Hardware/Software co-design para aplicações de processamento de voz - 4 Lista de Figuras Figura 1: Funções gerais do processamento de áudio do codec AMR...13 Figura 2: Diagrama de blocos simplificado do modelo de síntese de voz CELP...14 Figura 3: Diagrama de blocos simplificado do codificador de voz AMR...17 Figura 4: Diagrama de blocos simplificado do descodificador de voz AMR....19 Figura 5: Diagrama de blocos simplificado do algoritmo VAD....20 Figura 6: Diagrama de blocos de uma ligação com operação SCR...21 Figura 7 - OpenRISC 1200 Core...31 Figura 8 - Blocos que constituem o CPU/DSP Core...33 Figura 9: Estrutura da cache...36 Figura 10 CvsGet da página www.opencores.org...39 Figura 11: Esquema que representa o Core do CPU...42 Figura 12: Organização do top-level do Or1k (slices ocupadas por cada módulo)...45 Figura 13: Configuração do OpenRisc 1200 no seu top-level e interface com o exterior...49 Figura 14: Resultados obtidos ao ser invocado o or32-uclinux-objdump S mul...58 Figura 15:Resultados obtidos ao ser invocado o or32-uclinux-readelf a mul...60 Figura 16: Resultados da execução do comando or32-uclinux-readelf x 1 mul...60 Figura 17:Resultados obtidos ao ser invocado o or32-uclinux-size -- radix=16 mul ( formatação em hexadecimal)...61 Figura 18: Configuração da memória usada no simulador...65 Figura 19:Resultado da execução do programa de teste mul...67 Figura 20: Resultado da simulação do ficheiro representado acima...69 Figura 21: Interface necessário para realizar a comunicação com a memória da Cypress...84 Figura 22: Modelo pretendido em relação ao controlador de memórias...85 Figura 23:Arquitectura do controlador de memórias...86 Figura 24:Registos de configuração presentes no controlador de memórias...90 Figura 25:Modelo usado no testbench...92 Figura 26: Modo de geração dos endereços quando se realizam bursts...94 Figura 27: Timings e formas de onda associados à leitura da memória da Micron...94 Figura 28:Timings e formas de onda que estão associados à memória da Micron...95 Figura 29: Programação da mascara para o base address e do control status register...96 Figura 30:Configuração do CSC register e TMS register...96 Figura 31: Resultado da execução do testbench default relativo ao memory controller...97 Figura 32: Formas de onda associadas à escrita/leitura usando o protocolo wishbone...99 Figura 33:Descrição dos sinais de wishbone...100 Figura 34: Memória instanciada no sistema...100 Figura 35: Resultado da simulação em relação aos sinais da SSRAM...101 Figura 36: Modificação para fazer uso da memória da Cypress...102 Figura 37: Forma de ondas obtidas com a memória da Cypress...102 Figura 38 Configuração do Wishbone builder...104 Figura 39: Modelo Master Model para teste...105 Figura 40 : Teste do master model com acessos alternados...106 Figura 41: Resultados da execução do testbench com acesso concorrente dos...107

Hardware/Software co-design para aplicações de processamento de voz - 5

Hardware/Software co-design para aplicações de processamento de voz - 6 Lista de Tabelas Tabela 1: Alocação de bits do algoritmo do codec AMR para uma frame de 20 ms...18 Tabela 2: Estruturas existentes no codificador de voz AMR....26 Tabela 3: Estruturas existentes no descodificador de voz AMR...27 Tabela 4: Estrutura da chamada das funções do codificador...28 Tabela 5: Estrutura da chamada das funções da função cbsearch....29 Tabela 6: Estrutura da chamada das funções da função gainquant....29 Tabela 7: Estrutura da chamada das funções do descodificador....30 Tabela 8: Macros e respectiva interferência no sistema...40 Tabela 9: Módulos presentes no CPU Core, correspondência com ficheiro e respectiva funcionalidade...43 Tabela 10: Ficheiro e funcionalidade correspondentes para um determinado módulo do Or1k...46 Tabela 11: Slices ocupadas por cada uma das memórias...49 Tabela 12:Comandos disponíveis no simulador e respectiva funcionalidade...64 Tabela 13: Secções que constituem o sim.cfg e que podem sofrer alterações...67 Tabela 14: Operações básicas usadas pela norma do codec AMR...72 Tabela 15: Peso computacional do codec AMR em WMOPS (weighted million operations per second)...74 Tabela 16: Peso computacional do codificador de voz AMR no modo 12.2 kbit/s em WMOPS....75 Tabela 17: Peso computacional do descodificador de voz AMR no modo 12.2 kbit/s em WMOPS....75 Tabela 18: Posições potenciais dos pulsos individuais no codebook algébrico para o modo 12.2 kbit/s....76 Tabela 19: Número de ocorrências médias das operações básicas do codec no modo 12.2 kbit/s por frame....79 Tabela 20: Codificação definida pela lei A....80 Tabela 21: Configuração do Chip select register para uma memória SSRAM...88 Tabela 22: Lista de sinais associados ao protocolo wishbone e respectiva funcionalidade (obtidos a partir do documento de wishbone)...98 Tabela 23: Número de instruções obtido da simulação para o codificador e descodificador.126 Tabela 24: MIPS médio para codificador e descodificador...126

Hardware/Software co-design para aplicações de processamento de voz - 7 Ficheiros de Apoio Ficheiro de apoio 1: default.ld- contém as definições das diversas regiões de memória e endereços iniciais...55 Ficheiro de apoio 2:Makefile que permite a criação do binário a ser usado para a simulação.56 Ficheiro de apoio 3: Aspecto do simulador. São evidenciados os registos e as instruções do processador....62 Ficheiro de apoio 4: Ficheiro que mostra a potencialidade das funções referidas...69

Hardware/Software co-design para aplicações de processamento de voz - 8 Anexos Anexo 1:Versão simplificada do algoritmo do codificador ficheiro s10_8pf.c...132 Anexo 2:Contagem das operações básicas da norma do codec ficheiro count.c...139 Anexo 3: Implementação da lei A em código C...145 Anexo 4:Top level do CPU...152 Anexo 5: Instruções de vírgula fixa do processador Or1k...153 Anexo 6: Funções básicas da norma do codec implementadas em asssembly...160 Anexo 7: tt_ssram.v...169 Anexo 8: bench.v...172 Anexo 9: wb.vhd (descrição do master slave)...179 Anexo 10: Wishone.defines (definições para a elaboração do master slave)...183 Anexo 11: Modelo verilog da memória da Cypress...184 Anexo 12: Modelo verilog da memória da Micron...191 Anexo 13: bench1.v...195 Anexo 14: bench2.v...204 Anexo 15: Or1k TOP level...214 Anexo 16: mc_defines.v...215 Anexo 17 : Top level do master slave...219

Hardware/Software co-design para aplicações de processamento de voz - 9 1 Sumário A sociedade moderna assenta a sua dinâmica nas telecomunicações e contacto em tempo real. Por essa razão vários sistemas capazes de realizar a codificação de voz têm sido desenvolvidos. Integrado no contexto mencionado, neste projecto pretende-se realizar a implementação de um codificador de voz de débito variável, usando um processador dedicado. A linha orientadora assenta sobre a optimização quer a nível de software, a que corresponde a modificação de algoritmos, quer a nível de hardware, onde a arquitectura do processador é refinada para a aplicação desenvolvida. Este codec tem a capacidade de baixar ou aumentar o seu débito mediante as maiores ou menores interferências no canal de comunicação. Nas condições referidas, a codificação de canal contra erros será maior no caso de interferências mais acentuadas, garantindo-se assim que o bom desempenho do codec seja mantido. A arquitectura do processador usado é de domínio público, tendo sido desenhada com o objectivo de implementar um sistema capaz de ter consumo de potência reduzido, simplicidade, versatilidade e reduzida área. A avaliação das vantagens e desvantagens da utilização do modelo open-source no desenvolvimento de sistemas electrónicos dedicados é um dos motivos nucleares.

Hardware/Software co-design para aplicações de processamento de voz - 10 2 Introdução Neste projecto pretendeu-se realizar a implementação da norma 3GPP TS 26.071 V6.0.0 de codificação de audio para UMTS usando o processador OpenRisc 1200. A arquitectura do processador OpenRISC 1200 é de domínio público, constituindo a base para as famílias de processadores do tipo RISC/DSP de 32-bit e 64-bit. Este codec foi escolhido pela Third Generation Partnership Project (3GPP) como o codec principal para os sistemas da 3ª geração. A filosofia por detrás do AMR é baixar o seu débito assim que as interferências aumentem, possibilitando desta forma uma maior ou menor codificação de canal contra erros. Neste trabalho fez-se o estudo do OR1k, relativamente ao nível de tranferência lógica de registos, verificando qual era a sua caracterização em termos de performance, velocidade e área relativamente a um conjunto de módulos que lhe estavam associados. Essa caracterização é de tal forma importante, que sem a mesma, não se poderia adaptar este processador para a plataforma de desenvolvimento. Na parte do codec, fez-se um profiling das funções que ocupam mais processamento e implementou-se uma modificação no sentido de realizar optimizações a nível computacional com resultados comprovados. Numa outra fase, dedicaram-se os esforços para conseguir ter as ferramentas orientadas a este processador em funcionamento. Fizeram-se testes de pequenos segmentos de código e com base neles compreendeu-se a forma de poder interagir com o compilador e simulador, disponíveis de forma livre. Um teste mais profundo foi a implementação da lei A, onde na parte de software se fizeram as alterações necessárias à implementação do mesmo na arquitectura deste mesmo processador. Por outro lado, observou-se como funcionava a memória deste processador, registos e o assembly gerado com base no código C. A fase que mais recursos consumiu constitui a adaptação do código desta norma para a arquitectura do processador, onde diversas modificações tiveram de ser implementadas. Por outro lado foi necessário integrar o OR1k no sistema oferecido pela plataforma de desenvolvimento, tendo-se encontrado aí dificuldades.

Hardware/Software co-design para aplicações de processamento de voz - 11 Após ter sido terminada a adaptação da norma para a arquitectura, procedeu-se à sua caracterização em termos de instruções executadas no simulador, verificando-se que de facto uma variedade de optimizações podia ser implementada. Essas optimizações passaram pela escrita de funções em assembly, revelando-se uma solução poderosa. A integração do or1k continua, passando pela introdução de um controlador de memória, um master/slave capaz de estabelecer prioridades nos barramentos de dados, até chegar a um modelo final em que o core do OR1k tem acesso à memoria presente na plataforma de desenvolvimento. A optimização da norma revelou ser de facto espantosa em termos do número de instruções agora necessárias, cerca de 2,3 x menos existindo um caminho disponível para melhorar ainda mais este valor. No caso de hardware, o teste de um modelo capaz de verificar se o sistema integrado do or1k, master/slave e memory controller é capaz de funcionar e correr um programa compilado para este processador, foi a ultima tarefa que se pretendeu cumprir.

Hardware/Software co-design para aplicações de processamento de voz - 12 3 O codec de voz AMR 3.1 Descrição geral Desenvolvido pela European Telecommunications Standards Institute (ETSI) e standardizado para GSM, este codec foi escolhido pela Third Generation Partnership Project (3GPP) como o codec principal para os sistemas da 3ª geração. A filosofia por detrás do AMR é baixar o seu débito assim que as interferências aumentem, possibilitando desta forma uma maior ou menor codificação de canal contra erros. O AMR também é usado para harmonizar standards entre diferentes sistemas de telecomunicações celulares. O codec de voz AMR consiste assim num codificador de voz de débitos múltiplos, um esquema capaz de controlar e gerir os diferentes débitos através dum detector de presença de voz e um gerador de ruído de conforto, e um mecanismo de cancelamento de erros para combater os efeitos provocados por erros de transmissão. Este codec integra oito diferentes débitos desde 4.75 kbit/s a 12.2 kbit/s para codificar voz propriamente dita e um modo de baixo débito para codificar ruído de fundo. O codificador de voz é capaz de variar o seu débito a cada frame de 20ms. Toda a descrição detalhada deste codec pode ser encontrada no site da 3GPP em http://www.3gpp.org/ftp/specs/html-info/26-series.htm Aqui podem ser encontrados todos os documentos que descrevem esta norma em todas as suas vertentes. Na figura seguinte é possível ver-se aquilo que foi descrito anteriormente. Note-se que a fonte do sinal a ser codificada tem que surgir no formato PCM de 13 bits. Caso o sinal de entrada esteja codificado de acordo com a lei A, este terá que ser convertido para PCM de forma a ser codificado correctamente.

Hardware/Software co-design para aplicações de processamento de voz - 13 GSM 06.82.AMR GSM 06.81.AMR BSS side only GSM 06.60.AMR Voice Activity Detector 3 1 8bit / A-law to 13-bit uniform LPF A/D 2 Speech Encoder VAD GSM 06.60.AMR 6 4 Speech frame DTX Control and Operation SP flag 6 7 MS side only TRANSMIT SIDE GSM 03.50 Comfort Noise TX Functions GSM 06.62.AMR 5 SID frame Info. bits GSM 06.81.AMR GSM 06.61.AMR Info. bits 8 Speech frame substitution BSS side only GSM 06.60.AMR BFI 9 GSM 06.60.AMR 13-bit uniform to 8bit / A-law 1 SID 10 DTX Control and Operation 4 Speech Decoder 2 TAF Speech frame D/A LPF 11 GSM 06.62.AMR 5 SID frame Comfort Noise RX Functions MS side only RECEIVE SIDE GSM 03.50 Figura 1: Funções gerais do processamento de áudio do codec AMR. 1) 8-bit A-law or µ-law PCM (ITU-T Recommendation G.711), 8 000 samples/s; 2) 13-bit uniform PCM, 8 000 samples/s; 3) Voice Activity Detector (VAD) flag; 4) Encoded speech frame, 50 frames/s, number of bits/frame depending on the AMR codec mode; 5) SIlence Descriptor (SID) frame; 6) TX_TYPE, 2 bits, indicates whether information bits are available and if they are speech or SID information;

Hardware/Software co-design para aplicações de processamento de voz - 14 7) Information bits delivered to the 3G AN; 8) Information bits received from the 3G AN; 9) RX_TYPE, the type of frame received quantized into three bits. 3.2 Princípios do codificador de voz AMR O codec AMR consiste em oito diferentes fontes de débitos: 12.2, 10.2, 7.95, 7.40, 6.70, 5.90, 5.15 e 4.75 kbit/s. Este codec é baseado no modelo CELP (code-excited linear predictive). É usado um filtro de síntese obtido por uma análise de 10ª ordem dos parâmetros de predição linear, dados pela seguinte formúla: 1 1 Hz ( ) = Az $ = ( ) m 1+ az $ i = 1 onde â i são os parâmetros LP quantizados e m = 10 é a ordem de predição. A síntese de pitch é feita recorrendo-se ao seguinte filtro: 1 1 = Bz ( ) 1 g z onde T é o atraso do pitch e g p o seu ganho. Este filtro é implementado usando técnicas adaptativas. p T i i O modelo de síntese deste codec de voz é ilustrado na figura seguinte: adaptive codebook g p v(n) fixed codebook + u(n) 1 s(n) ^ A(z) post-filtering s'(n) ^ g c c(n) LP synthesis Figura 2: Diagrama de blocos simplificado do modelo de síntese de voz CELP.

Hardware/Software co-design para aplicações de processamento de voz - 15 Neste modelo, o sinal de excitação à entrada do filtro de síntese LP é construído adicionando-se dois vectores de excitação: um do codebook fixo e outro do codebook adaptativo. O sinal de fala é sintetizado fazendo-se passar pelo filtro LP o sinal proveniente da soma dos codebooks. O sinal de excitação óptimo é obtido fazendo-se uma análise por síntese onde o erro entre o sinal original e o sintetizado é minimizado de acordo com uma medida de distorção pesada perceptual. O filtro de distorção perceptual usado na técnica de análise por síntese é dado por: Az Wz ( ) = Az ( γ 1) ( γ ) onde A(z) é o filtro LP não quantizado e 0 < γ 2 <γ 1 1 são os pesos dos factores perceptuais. Valores de γ 1 = 0.9 (para os modos de 12.2 e 10.2 kbit/s) ou γ 1 = 0.94 (para os restantes modos) e γ 2 = 0.6 são usados. O codificador opera com frames de fala de 20 ms cada, correspondendo a 160 amostras obtidas a um frequência de amostragem de 8000 amostras por segundo. A cada 160 amostras, o sinal é analisado de forma a extrair os parâmetros do modelo CELP: coeficientes do filtro LP, índices e ganhos dos codebooks adaptativo e fixo. Estes parâmetros são codificados e transmitidos. No descodificador, estes parâmetros são descodificados e o sinal de fala é sintetizado fazendo-se passar o sinal de excitação (codebook adaptativo mais codebook fixo) pelo filtro de síntese LP. No diagrama seguinte ilustra-se todas as operações realizadas pelo codificador. Análise LP é realizada duas vezes por frame para o modo de 12.2 kbit/s e uma vez para os restantes modos. Para o modo 12.2 kbit/s, dois conjuntos de parâmetros LP são convertidos para line spectral pairs (LSP) e quantizados juntamente usando split matrix quantization (SMQ) num total de 38 bits. Para os outros modos, é feita uma análise LP que é convertida para LSP e quantizada usando split vector quantization (SVQ). A frame do sinal de fala é dividida em 4 subframes de 5 ms cada (40 amostras). Os parâmetros do codebook fixo e adaptativo são transmitidos em cada subframe. Os parâmetros LP quantizados e não quantizados ou suas versões interpoladas são usadas dependendo da subframe. Um atraso de pitch é estimado em open-loop em cada 2

Hardware/Software co-design para aplicações de processamento de voz - 16 subframe (excepto para os modos 5.15 e 4.75 kbit/s onde é feito uma vez por frame) baseado no sinal de fala perceptualmente pesado. De seguida as seguintes operações são repetidas para cada subframe: O sinal objectivo x(n) é calculado filtrando o sinal residual LP através do filtro de síntese pesado W(z)H(z) com o estado inicial dos filtros sendo actualizados pela filtragem do erro entre o LP residual e a excitação (isto é equivalente a subtrair a resposta do filtro de síntese pesado a uma entrada nula ao sinal de fala pesado). A resposta impulsional, h(n) do filtro de síntese pesado é calculada; Uma análise, dita closed-loop, do pitch é realizada (para encontrar o atraso e ganho do pitch), usando o sinal objectivo x(n) e a resposta impulsional h(n), pesquisando em torno do atraso obtido no open-loop pitch. Pitch fraccionários com 1/6 ou 1/3 de resolução de uma amostra (dependendo de modo) são usados; O sina objectico x(n) é actualizado removendo-se a contribuição do codebook adaptativo, e este novo sinal objectivo, x2(n), é usado na pesquisa do codebook algébrico (inovativo), por forma a encontrar o melhor codebook; Os ganhos do codebook adaptativo e inovativo são quantificados escalarmente com 4 e 5 bits respectivamente ou quantificados com 6 ou 7 bits (com predição do movimento da média aplicada ao ganho do codebook fixo); Finalmente, as memórias dos filtros são actualizadas (usando o sinal de excitação determinado) para encontrar o sinal na próxima subframe.

Hardware/Software co-design para aplicações de processamento de voz 17 Pre-processing Pre-processing windowing and autocorrelation R[ ] Levinson- Durbin R[ ] A(z) A(z) LSP LSP indices LSP quantization interpolation for the 4 subframes LSP A(z) ^ frame subframe LPC analysis (twice per frame) Open-loop pitch search Adaptive codebook (twice per frame) search Innovative codebook search s(n) interpolation for the 4 subframes LSP A(z) A(z) compute weighted speech (4 subframes) find open-loop pitch A(z) ^ A(z) compute target for adaptive codebook x(n) T o find best delay and gain h(n) quantize LTP-gain compute adaptive codebook contribution x(n) pitch index LTP gain index compute target for innovation x (n) 2 find best innovation code index A(z) ^ A(z) compute impulse response h(n) Filter memory update update filter memories for next subframe compute excitation fixed codebook gain quantization fixed codebook gain index Figura 3: Diagrama de blocos simplificado do codificador de voz AMR. A alocação de bits para o codec AMR nos seus diferentes modos é mostrada na tabela seguinte. Em cada 20 ms de sinal de fala, 95, 103, 118, 134, 148, 159, 204 ou 244

Hardware/Software co-design para aplicações de processamento de voz 18 bits são produzidos, correspondendo aos débitos de 4.75, 5.15, 5.90, 6.70, 7.40, 7.95, 10.2 ou 122.2 kbit/s. Mode Parameter 1st subframe 2nd subframe 3rd subframe 4th subframe total per frame 2 LSP sets 38 12.2 kbit/s Pitch delay 9 6 9 6 30 (GSM EFR) Pitch gain 4 4 4 4 16 Algebraic code 35 35 35 35 140 Codebook gain 5 5 5 5 20 Total 244 LSP set 26 10.2 kbit/s Pitch delay 8 5 8 5 26 Algebraic code 31 31 31 31 124 Gains 7 7 7 7 28 Total 204 LSP sets 27 7.95 kbit/s Pitch delay 8 6 8 6 28 Pitch gain 4 4 4 4 16 Algebraic code 17 17 17 17 68 Codebook gain 5 5 5 5 20 Total 159 LSP set 26 7.40 kbit/s Pitch delay 8 5 8 5 26 (TDMA EFR) Algebraic code 17 17 17 17 68 Gains 7 7 7 7 28 Total 148 LSP set 26 6.70 kbit/s Pitch delay 8 4 8 4 24 (PDC EFR) Algebraic code 14 14 14 14 56 Gains 7 7 7 7 28 Total 134 LSP set 26 5.90 kbit/s Pitch delay 8 4 8 4 24 Algebraic code 11 11 11 11 44 Gains 6 6 6 6 24 Total 118 LSP set 23 5.15 kbit/s Pitch delay 8 4 4 4 20 Algebraic code 9 9 9 9 36 Gains 6 6 6 6 24 Total 103 LSP set 23 4.75 kbit/s Pitch delay 8 4 4 4 20 Algebraic code 9 9 9 9 36 Gains 8 8 16 Total 95 Tabela 1: Alocação de bits do algoritmo do codec AMR para uma frame de 20 ms. 3.3 Princípios do descodificador de voz AMR No diagrama da figura seguinte é ilustrado o processo de descodificação e síntese dum sinal de fala produzido pelo descodificador AMR.

Hardware/Software co-design para aplicações de processamento de voz 19 frame subframe post-processing LSP indices decode LSP interpolation of LSP for the 4 subframes pitch index gains indices code index decode adaptive codebook decode innovative codebook decode gains construct excitation synthesis filter s(n) ^ post filter ^ s'(n) LSP ^ A(z) Figura 4: Diagrama de blocos simplificado do descodificador de voz AMR. Aqui, dependendo do modo escolhido, os índices transmitidos são extraídos do bitstream recebido. Estes índices são descodificados para obter os parâmetros de codificação a cada frame transmitida. Os parâmetros são os vectores LSP, os atrasos fraccionários do pitch, o codebook inovativo e os ganhos do pitch e codebook inovativo. Os vectores LSP são convertidos nos coeficientes do filtro LP e interpolados para obter filtros LP em cada subframe. Então, a cada 40 amostras (subframe) é realizado o seguinte: A excitação é construída por adição do codebook inovativo e adaptativo escalados dos seus respectivos ganhos; O sinal de fala é reconstruído por filtragem da excitação através do filtro de síntese LP. Finalmente, o sinal de fala reconstruído é filtrado por uma pós-filtragem adaptativa. 3.4 Detector de presença de voz (VAD) O algoritmo VAD (voice activity detector) usa os parâmetros do codificador de voz para calcular uma flag que simplesmente indique a presença ou não de um sinal de voz.

Hardware/Software co-design para aplicações de processamento de voz 20 No esquema seguinte pode-se ver um diagrama de blocos representativo deste algoritmo. s(i) Filter bank and computation of sub-band levels level[n] T_op[n] Pitch detection pitch tone VAD decision VAD_flag t0,t1 Tone detection complex_warning OL-LTP correlation vector Complex signal analysis complex_timer Figura 5: Diagrama de blocos simplificado do algoritmo VAD. Amostras da frame de entrada, s(i), são divididas em sub-bandas e o nível do sinal para cada sub-banda é então calculado. A entrada do bloco de detecção de pitch, T_op[n], é o resultado do atraso do pitch calculado no codificador de voz. Este bloco calcula uma flag (pitch) que indica a presença ou não de pitch. O bloco de detecção de tom calcula uma flag (tone), que indica a presença de um tom de informação. Tons são detectados tendo como base o ganho de pitch obtido na análise em open-loop pelo codificador de voz. A detecção de sinais complexos calcula uma flag (complex_warning), indicando a presença de sinais complexos como, por exemplo, música. Esta detecção é feita tendo como base o vector de correlação disponível na análise em open-loop do pitch. A função de decisão de VAD estima níveis de ruído de fundo. Estes níveis são comparados com os níveis da frame de entrada (level[n]) por forma a tomar-se uma decisão intermédia. Finalmente, a decisão final é tomada tendo em consideração decisões tomadas anteriormente.

Hardware/Software co-design para aplicações de processamento de voz 21 3.5 Controlo do débito (SCR) A operação SCR (source controlled rate) é o mecanismo que permite a um codificador de voz AMR codificar com um débito médio inferior, tendo em conta os períodos de inactividade de voz. Este esquema é particularmente útil para se poder poupar energia nos equipamentos terminais, uma vez que quando não há actividade de voz, o processamento sobre o sinal fica simplificado para reduzir cargas de débitos a que as redes de comunicações ficam sujeitas. Esta operação requer do lado do emissor um detector de voz e um avaliador do ruído de fundo a ser transmitido. Do lado de receptor terá que existir um gerador de ruído de fundo (ruído de conforto), durante os períodos onde a transmissão está desligada. No esquema seguinte pode ver-se uma ligação entre emissor e receptor usando uma operação SCR. TX SCR handler Network RX SCR handler Speech Encoder Voice Activity Detector Comfort Noise Parameter Computation Information bits Mode Indication TX_TYPE Information packeting, transport and classification Information bits Mode Indication RX_TYPE Figura 6: Diagrama de blocos de uma ligação com operação SCR. Speech Decoder Error Concealment Comfort Noise Generation De notar que, em adição a estas funcionalidades, se os parâmetros chegados ao receptor forem detectados como estando seriamente corrompidos por erros, o sinal de fala ou de ruído de conforto deve ser substituído por outros dados de forma a evitar efeitos desagradáveis para o ouvinte. 3.6 Código C para o codec de voz AMR Esta é a parte da norma que engloba as diferentes componentes que constituem o codec AMR. Aqui, foi realizada uma implementação em vírgula fixa das funções que codificam a voz, o detector de presença de voz, ruído de conforto, controlo de débito e substituição de frames perdidas.

Hardware/Software co-design para aplicações de processamento de voz 22 De salientar que é esta parte da norma que realmente define todo o codec de voz AMR, ou seja, em caso de incoerência por parte de alguma descrição que seja feita do codec face ao que é realmente feito na sua versão em código C, prevalece este último. 3.6.1 Conteúdos do código C Os ficheiros que constituem o código C, distribuem-se em 3 tipo: os com o sufixo c com o código propriamente dito e os respectivos header files com sufixo h. Os dados encontram-se, de uma forma geral, nos ficheiros com o sufixo tab. Para se proceder à instalação do programa basta correr o makefile que acompanha os restantes ficheiros. Uma vez instalado o software, são gerados dois executáveis: encoder e decoder, responsáveis respectivamente pela codificação e descodificação dum sinal de acordo com a norma AMR. Para além disto, existem ficheiros para verificar a correcta instalação do codificador e descodificador. Destes, interessam os ficheiros spch_dos.inp, spch_dos.cod e spch_dos.out, que são respectivamente ficheiros de entrada de um sinal de áudio, a sua versão codificada e à saída do descodificador. Para se testar a instalação realizado é fornecido um script que automaticamente corre o programa instalado comparando os resultados obtidos com os dos ficheiros descritos anteriormente. Este script encontra-se no ficheiro amr_chk.csh que deve ser chamado numa shell da seguinte forma:./amr_chk.csh dos. A instalação correcta do código num PC correu sem dar qualquer tipo de problemas. 3.6.2 Execução do programa A execução do codificador e descodificador deve ser realizada da seguinte forma: - encoder [options] amr_mode input_file bitstream_file; amr_mode = MR475 4.75 kbit/s MR515 5.15 kbit/s MR59 5.90 kbit/s MR67 6.70 kbit/s MR74 7.40 kbit/s MR795 7.95 kbit/s MR102 10.20 kbit/s MR122 12.20 kbit/s options = -dtx habilita operação com débito variável

Hardware/Software co-design para aplicações de processamento de voz 23 - decoder [option] bitstream_file output_file; option = -rxframetype esperada frame RX em vez de TX. Útil apenas para simulações com outros componentes entre codificador e descodificador. Os ficheiros de fala contêm dados de amostras em formato PCM de 16 bits e ficheiros de bitstream os dados codificados e algumas flags adicionais. 3.6.3 Estrutura do código O código C é estruturado seguindo a seguinte estrutura. Cada função que necessita de variáveis estáticas é considerada um módulo. Este módulo consiste em: - uma estrutura (struct) combinando as diversas variáveis do módulo; - três funções auxiliares: func_init(), func_reset() e func_exit(); - o processamento da função func() propriamente dita. A inicialização da função func_init() aloca um novo estado da estrutura, chama a função func_reset(), guarda o apontador que aponta para a nova estrutura alocada e retorna o valor de 0 se a operação correu bem ou 1 caso contrário. A função de reset, func_reset(), utiliza o apontador da estrutura em causa colocando os seus membros para um valor predefinido. A função exit, func_exit(), realiza a libertação de memória que tinha sido previamente realizada. A função propriamente dita, func(), pega nesse apontador assim como outros parâmetros fundamentais, e realiza as operações necessárias para obter os resultados finais que podem passar pela alteração dos campos dessa estrutura. Um módulo pode chamar outro módulo. Neste caso, o de maior nível terá que conter um apontador para o de menor nível e as funções de init, reset e exit são chamadas recursivamente. Nas duas tabelas seguintes é possível ver-se todas as estruturas que existem ao longo dos programas de codificação e descodificação.

Hardware/Software co-design para aplicações de processamento de voz 24 Struct name Variable Type[Length] Description Speech_Encode_ cod_amr_state cod_amrstate see below in this table FrameState pre_state Pre_ProcessState see below in this table dtx Flag Is set if DTX functionality is used complexitycounter int Used for wmops counting Pre_ProcessState y2_hi Word16 filter state, upper word y2_lo Word16 filter state, lower word y1_hi Word16 filter state, upper word y1_lo Word16 filter state, lower word x0 Word16 filter state x1 Word16 filter state cod_amrstate old_speech Word16[320] speech buffer speech Word16* pointer to current frame in old_speech p_window Word16* pointer to LPC analysis window in old_speech p_window_12k2 Word16* pointer to LPC analysis window with no lookahead in old_speech (MR122) new_speech Word16* pointer to the last 160 speech samples in old_speech old_wsp Word16[303] buffer holding spectral weighted speech wsp Word16* pointer to the current frame in old_wsp old_lags Word16[5] open loop LTP states ol_gain_flg Word16[2] enables open loop pitch lag weighting (MR102) old_exc Word16[314] excitation vector exc Word16* current excitation ai_zero Word16[51] history of weighted synth. filter followed by zero vector zero Word16* zero vector h1 Word16* impulse response of weighted synthesis filter hvec Word16[80] zero vector followed by impulse response lpcst lpcstate see below in this table lspst lspstate see below in this table clltpst clltpstate see below in this table gainquantst gainquantstate see below in this table pitcholwghtst pitcholwghtstate see below in this table tonstabst tonstabstate see below in this table vadst vadstate1 see below in this table vadst vadstate2 see below in this table dtx Flag is set if DTX functionality is used dtx_encst dtx_encstate see below in this table mem_syn Word16[10] synthesis filter memory mem_w0 Word16[10] weighting filter memory (applied to error signal) mem_w Word16[10] weighting filter memory (applied to input signal) mem_err Word16[50] filter memory for production of error vector error Word16* error signal (input minus synthesized speech) sharp Word16 pitch sharpening gain vadstate1 bckr_est Word16[9] background noise estimate ave_level Word16[9] averaged input components for stationary estimation old_level Word16[9] input levels of the previous frame sub_level Word16[9] input levels calculated at the end of a frame (lookahead) a_data5 Word16[6] memory for the filter bank a_data3 Word16[5] memory for the filter bank burst_count Word16 counts length of a speech burst hang_count Word16 hangover counter stat_count Word16 stationary counter vadreg Word16 15 flags for intermediate VAD decisions pitch Word16 15 flags for pitch detection tone Word16 15 flags for tone detection complex_high Word16 flags for complex detection complex_low Word16 flags for complex detection oldlag_count Word16 variables for pitch detection oldlag Word16 variables for pitch detection complex_hang_count Word16 complex hangover counter, used by VAD complex_hang_timer Word16 hangover initiator, used by CAD

Hardware/Software co-design para aplicações de processamento de voz 25 Struct name Variable Type[Length] Description best_corr_hp Word16 filtered value speech_vad_decision Word16 final decision complex_warning Word16 complex background warning sp_burst_count Word16 counts length of a speech burst incl HO addition corr_hp_fast Word16 filtered value vadstate2 pre_emp_mem Word16 input pre-emphasis memory update_cnt Word16 noise update counter hyster_cnt Word16 hysteresis counter last_update_cnt Word16 noise update counter value for last frame ch_enrg_long_db Word16[16] long term channel energy in db Lframe_cnt Word32 10 ms frame counter Lch_enrg Word32[16] channel energy estimate Lch_noise Word32[16] channel noise estimate last_normb_shift Word16 block shift factor for last frame, used for pre_emp_mem tsnr Word16 total estimated peak SNR in db hangover Word16 VAD hangover burstcount Word16 number of consecutive voice active frames fupdate_flag Word16 A flag to control a forced update of the noise estimate negsnrvar Word16 SNR variability negsnrbias Word16 sensitivity bias shift_state Word16 indicates scaling state of channel energy estimate L_R0 Word32 LTP energy L_Rmax Word32 LTP max correlation LTP_flag Flag set when open loop pitch prediction gain > threshold dtx_encstate lsp_hist Word16[80] LSP history (8 frames) log_en_hist Word16[8] logarithmic frame energy history (8 frames) hist_ptr Word16 pointer to the cyclic history vectors log_en_index Word16 Index for logarithmic energy init_lsf_vq_index Word16 initial index for lsf predictor lsp_index Word16[3] lsp indecies to the three code books dtxhangovercount Word16 is decreased in DTX hangover period decanaelapsedcount Word16 counter for elapsed speech frames in DTX lpcstate LevinsonSt LevinsonState see below LevinsonState old_a Word16[11] last frames direct form coefficients lspstate lsp_old Word16[10] old LSP vector lsp_old_q Word16[10] old quantized LSP vector qst Q_plsfState see below in this table Q_plsfState past_rq Word16[10] past quantized LSF prediction error clltpstate pitchst Pitch_frState see below in this table tonstabstate count Word16 count consecutive (potential) resonance frames gp Word16[7] pitch gain history Pitch_frState T0_prev_subframe Word16 integer. pitch lag of previous subframe gainquantstate sf0_exp_gcode0 Word16 subframe 0/2 codebook gain exponent sf0_frac_gcode0 Word16 subframe 0/2 codebook gain fraction sf0_exp_target_en Word16 subframe 0/2 target energy exponent sf0_frac_target_en Word16 subframe 0/2 target energy fraction sf0_exp_coeff Word16[5] subframe 0/2 energy coefficient exponents sf0_frac_coeff Word16[5] subframe 0/2 energy coefficient fractions gain_idx_ptr Word16* pointer to gain index value in parameter frame gc_predst gc_predstate see below in this table gc_preduncst gc_predstate see below in this table adaptst GainAdaptState see below in this table gc_predstate past_qua_en Word16[4] MA predictor memory (20*log10(pred. error)) past_qua_en_mr122 Word16[4] MA predictor memory, 12.2 style (log2(pred. error)) GainAdaptState onset Word16 onset counter prev_alpha Word16 previous adaptor output prev_gc Word16 previous codebook gain ltpg_mem Word16[5] pitch gain history

Hardware/Software co-design para aplicações de processamento de voz 26 Struct name Variable Type[Length] Description pitcholwghtstate old_t0_med Word16 weighted open loop pitch lag ada_w Word16 weigthing level depeding on open loop pitch gain wght_flg Word16 switches lag weighting on and off Tabela 2: Estruturas existentes no codificador de voz AMR. Struct name Variable Type[Length] Description decoder_amrstate Decoder_amrState see below in this table Speech_Decode_Fram estate post_state Post_FilterState see below in this table posthp_state Post_ProcessState see below in this table ComplexityCounter int Used for wmops counting Decoder_amrState old_exc Word16[194] excitation vector exc Word16* current excitation lsp_old Word16[10] LSP vector of previous frame mem_syn Word16[10] synthesis filter memory sharp Word16 pitch sharpening gain old_t0 Word16 pitch sharpening lag prev_bf Word16 previous value of "bad frame" flag prev_pdf Word16 previous value of "pot. dangerous frame" flag state Word16 ECU state (0..6) excenergyhist Word16[9] excitation energy history T0_lagBuff Word16 received pitch lag for ECU inbackgroundnoise Word16 background noise flag voicedhangover Word16 hangover flag ltpgainhistory Word16[9] pitch gain history background_state Bgn_scdState see below in this table Cb_gain_averState Cb_gain_averageStat see below in this table e lsp_avg_st lsp_avgstate see below in this table lsfstate D_plsfState see below in this table ec_gain_p_st ec_gain_pitchstate see below in this table ec_gain_c_st ec_gain_codestate see below in this table pred_state gc_predstate see table 7 nodataseed Word16 seed for CN generator ph_disp_st ph_dispstate see below in this table dtxdecoderstate dtx_decstate see below in this table dtx_decstate since_last_sid Word16 number of frames since last SID frame true_sid_period_inv Word16 inverse of true SID update rate log_en Word16 logarithmic frame energy old_log_en Word16 previous value of log_en L_pn_seed_rx Word32 random number generator seed lsp Word16[10] LSP vector lsp_old Word16[10] previous LSP vector lsf_hist Word16[80] LSF vector history (8 frames) lsf_hist_ptr Word16 index to beginning of LSF history lsf_hist_mean Word16[80] mean-removed LSF history (8 frames) log_pg_mean Word16 mean-removed logarithmic prediction gain log_en_hist Word16[8] logarithmic frame energy history log_en_hist_ptr Word16 index to beginning of log, frame energy history log_en_adjust Word16 mode-dependent frame energy adjustment dtxhangovercount Word16 counts down in hangover period decanaelapsedcount Word16 counts elapsed speech frames after DTX sid_frame Word16 flags SID frames valid_data Word16 flags SID frames containing valid data dtxhangoveradded Word16 flags hangover period at end of speech dtxglobalstate enum DTXStateType DTX state flags data_updated Word16 flags CNI updates Bgn_scdState frameenergyhist Word16[60] history of synthesis frame energy bghangover Word16 number of frames since last speech frame

Hardware/Software co-design para aplicações de processamento de voz 27 Struct name Variable Type[Length] Description Cb_gain_averageState cbgainhistory Word16[7] codebook gain history hangvar Word16 counts length of talkspurt in subframes hangcount Word16 number of subframes since last talkspurt lsp_avgstate lsp_meansave Word16[10] averaged LSP vector D_plsfState past_r_q Word16[10] past quantized LSF prediction vector past_lsf_q Word16[10] past dequantized LSF vector ec_gain_pitchstate pbuf Word16[5] pitch gain history past_gain_pit Word16 previous pitch gain (limited to 1.0) prev_gp Word16 previous good pitch gain ec_gain_codestate gbuf Word16[5] codebook gain history past_gain_code Word16 previous codebook gain prev_gc Word16 previous good codebook gain ph_dispstate gainmem Word16[5] pitch gain history prevstate Word16 previously used impulse response prevcbgain Word16 previous codebook gain lockfull Word16 force maximum phase dispersion onset Word16 onset counter Post_FilterState res2 Word16[40] LP residual mem_syn_pst Word16[10] synthesis filter memory synth_buf Word16[170] synthesis filter work area agc_state agcstate see below in this table preemph_state preemphasisstate see below in this table agcstate past_gain Word16 past agc gain preemphasisstate mem_pre Word16 filter state Post_ProcessState y2_hi Word16 filter state, upper word y2_lo Word16 filter state, lower word y1_hi Word16 filter state, upper word y1_lo Word16 filter state, lower word x0 Word16 filter state x1 Word16 filter state Tabela 3: Estruturas existentes no descodificador de voz AMR. 3.6.4 Hierarquia do código As quatro tabelas seguintes mostram toda a sequência de chamadas às funções existentes no código da norma do codec AMR. Estes quadros devem ser lidos da esquerda para a direita à medida que nível que o profundidade da chamada das funções vai sendo aumentando.

Hardware/Software co-design para aplicações de processamento de voz 28 Speech_Encode_Frame Pre_Process cod_amr Prm2bits Copy Vad1 filter_bank first_filter_stage filter5 filter3 level_calculation vad_decision complex_estimate_adapt complex_vad noise_estimate_update update_cntrl hangover_addition tx_dtx_handler lpc Autocorr Lag_window Levinson lsp Az_lsp Chebps Q_plsf_5 Lsp_lsf Lsf_wt Vq_subvec Vq_subvec_s Reorder_lsf Lsf_lsp Int_lpc_1and3_2 Lsp_az Get_lsp_pol Int_lpc_1and3 Lsp_az Get_lsp_pol Q_plsf_3 Lsp_lsf Lsf_wt Copy Vq_subvec3 Vq_subvec4 Reorder_lsf Lsf_lsp Int_lpc_1to3_2 Lsp_az Get_lsp_pol Int_lpc_1to3 Lsp_az Get_lsp_pol Copy dtx_buffer Copy Log2 Log2_norm dtx_enc Lsp_lsf Reorder_lsf Lsf_lsp Set_zero lsp_reset Copy Q_plsf_reset cl_ltp_reset Pitch_fr_reset check_lsp pre_big Weight_Ai Residu Syn_filt ol_ltp Pitch_ol vad_tone_detection_update Lag_max vad_pitch_detection subframepreproc Pitch_ol_wgh Weight_Ai Syn_filt Residu Copy cl_ltp Pitch_fr getrange Norm_Corr Pred_lt_3or6 Convolve G_pitch check_gp_clipping q_gain_pitch cbsearch ver Tabela 5 gainquant ver Tabela 6 update_gp_clipping Copy subframepostproc Syn_filt Pred_lt_3or6 Convolve Int2bin comp_corr hp_max 2 vad_complex_detection_update comp_corr Lag_max gmed_n hp_max vad_complex_detection_update searchfrac Enc_lag3 Enc_lag6 vad_tone_detection Inv_sqrt vad_tone_detection_update vad_tone_detection Convolve Inv_sqrt Interpol_3or6 Tabela 4: Estrutura da chamada das funções do codificador.

Hardware/Software co-design para aplicações de processamento de voz 29 cbsearch code_2i40_9bits cor_h_x set_sign cor_h search_2i40 build_code code_2i40_11bits cor_h_x set_sign cor_h search_2i40 build_code code_3i40_14bits cor_h_x set_sign cor_h search_3i40 build_code code_4i40_17bits cor_h_x set_sign cor_h search_4i40 build_code code_8i40_31bits cor_h_x set_sign12k2 cor_h search_10and8i40 build_code compress_code code_10i40_35bits cor_h_x set_sign12k2 cor_h Inv_sqrt Inv_sqrt Inv_sqrt Inv_sqrt Inv_sqrt Inv_sqrt compress10 Inv_sqrt Inv_sqrt search_10and8i40 build_code q_p Tabela 5: Estrutura da chamada das funções da função cbsearch. gainquant gc_pred_copy Copy gc_pred Log2 Log2_norm Log2_norm calc_filt_energies calc_target_energy MR475_update_unq_pred gc_pred_update MR475_gain_quant MR475_quant_store_results Log2 Log2_norm gc_pred_update gc_pred Log2 Log2_norm Log2_norm G_code q_gain_code Pow2 MR795_gain_quant q_gain_pitch MR795_gain_code_quant3 calc_unfilt_energies Log2 Log2_norm gain_adapt gmed_n MR795_gain_code_quant_mod sqrt_l_exp Qua_gain Pow2 gc_pred_update Tabela 6: Estrutura da chamada das funções da função gainquant.

Hardware/Software co-design para aplicações de processamento de voz 30 Speech_Decode_Frame Bits2prm Bin2int Decoder_amr rx_dtx_handler Decoder_amr_reset Post_Filter dtx_dec lsp_avg_reset D_plsf_reset ec_gain_pitch_reset ec_gain_code_reset gc_pred_reset Bgn_scd_reset ph_disp_reset dtx_dec_reset Copy Lsf_lsp Init_D_plsf_3 D_plsf_3 pseudonoise Lsp_lsf Reorder_lsf Lsp_Az A_Refl Log2 Build_CN_code Syn_filt Set_zero Copy Set_zero Copy Reorder_lsf Copy Lsf_lsp Get_lsp_pol Log2_norm pseudonoise Lsf_lsp lsp_avg Copy D_plsf_3 Reorder_lsf Copy Lsf_lsp Int_lpc_1to3 Lsp_Az Get_lsp_pol D_plsf_5 Reorder_lsf Copy Lsf_lsp Int_lpc_1and3 Lsp_Az Get_lsp_pol Dec_lag3 Pred_lt_3or6 Dec_lag6 decode_2i40_9bits decode_2i40_11bits decode_3i40_14bits decode_4i40_17bits decode_8i40_31bits decompress_code decompress10 ec_gain_pitch gmed_n d_gain_pitch ec_gain_pitch_update decode_10i40_35bits Dec_gain Log2 Log2_norm gc_pred Log2 Log2_norm Log2_norm Pow2 gc_pred_update ec_gain_code gmed_n gc_pred_average_limeted gc_pred_update ec_gain_code_update d_gain_code gc_pred Log2 Log2_norm Log2_norm Pow2 gc_pred_update Int_lsf Cb_gain_average ph_disp_release ph_disp_lock ph_disp sqrt_l_exp Ex_ctrl gmed_n agc2 Inv_sqrt Syn_filt Bgn_scd gmed_n dtx_dec_activity_update Copy Log2 Log2_norm lsp_avg Copy Weight_Ai Residu Set_zero Syn_filt Preemphasis agc energy_old energy_new energy_old Inv_sqrt Post_Process Tabela 7: Estrutura da chamada das funções do descodificador.

Hardware/Software co-design para aplicações de processamento de voz 31 4 OpenRISC 1200 RISC/DSP Core 4.1 Descrição do OpenRISC 1200 Core A arquitectura do processador OpenRISC 1200 é de domínio público, constituindo a base para as famílias de processadores do tipo RISC/DSP de 32-bit e 64-bit. Esta foi desenhada com o objectivo de implementar um sistema capaz de ter consumo de potência reduzido, simplicidade, versatilidade, reduzida área, capaz de servir aplicações a nível de redes e de SoC. As principais potencialidades deste processador incluem a existência de instruções do tipo DSP e Floating point e na versão de 64-bit instruções vectoriais. Possui ainda suporte para memória virtual, quer de dados quer de instruções, cache de dados e de instruções, e suporte para SMP (Symmetrical Multi-Processing) e SMT (Simultaneous Multi-Treading). Salientem-se ainda certas features dedicadas para funcionamento em rede e em sistemas embebidos, sendo que as mais notáveis são um número configurável de registos, cache e tamanhos das TLB (Translation Lookaside Buffer) configuráveis, controlo dinâmico da potência dinâmica e espaço para implementação de instruções fornecidas pelo utilizador. Numa perspectiva de alto nível este processador pode ser apresentado como um conjunto de unidades modulares. Esse esquema está apresentado na Figura 7 Figura 7 - OpenRISC 1200 Core

Hardware/Software co-design para aplicações de processamento de voz 32 O Or1k funciona com instruções ORBIS32 (OpenRISC based instruction set) pelo que a sua arquitectura é de 32-bit. Este processador tem uma pipeline de 5 andares (single-issue 5-stage), e a maior parte das suas instruções são executadas num único ciclo de relógio. Em termos das suas interrupções internas, o Or1k tem a capacidade de responder rapidamente e deterministicamente a estas mesmas. Existem no processador 32 registos para uso geral de 32-bits. Pode referir-se ainda que existe uma unidade de DSP/MAC integrada, que permite multiplicações entre operandos de 32-bits e com acumulador de 48-bits. Das implementações físicas que se fizeram deste processador em tecnologia específica foram obtidos 250 MHZ usando como dimensões mínimas 0.18u com 6 camadas de metal. O Or1k é optimizado apara integrar aplicações do tipo system-on-chip, que correspondem a sistemas embebidos. De forma sumária poder-se-á dizer que este processador serve aplicações que necessitem da performance de processadores de 32-bits em relação aos de 16, e em que factores como o consumo de potência e custo são determinantes. Para uma descrição mais detalhada relativa ao funcionamento de cada uma destas secções deve ser consultada tanto o manual que descreve a arquitectura deste processador, como o documento que contém as especificações do Or1k. Pretende-se nas secções seguintes fazer uma apresentação de cada um dois blocos que se encontra presente na figura e que pode caracterizar o processador. 4.1.1 CPU/DSP O esquema seguinte representa de forma mais detalhada o CPU/DSP.

Hardware/Software co-design para aplicações de processamento de voz 33 Figura 8 - Blocos que constituem o CPU/DSP Core Este bloco representa a parte central do processador OR1200 RISC. Esta versão apenas suporta 32-bit. Operações em vírgula flutuante não são suportadas, assim como as que têm carácter vectorial. Ter-se-á o cuidado de descrever cada um dos blocos que constitui a parte central deste CPU. 4.1.1.1 Unidade de Instruções A unidade de instruções implementa a pipeline básica das instruções, que as carrega a partir do subsistema da memória, direcciona-as para unidades válidas de execução, e mantém um histórico do seu estado, de modo a poder assegurar um modelo preciso de excepções e que as operações sejam terminadas com a ordem correcta. Também deve ser capaz de distinguir quando é que os dados estão disponíveis e certificar-se que não existe outra instrução a querer aceder ao mesmo registo de destino. Esta unidade suporta instruções da classe ORBIS32. 4.1.1.2 Registos para uso genérico (GPR) O OpenRISC 1200 possui, como referido na descrição sumária, 32 registos de 32- bits para uso genérico. A arquitectura que lhe está associada permite a cópia para um ficheiro que contém a informação sobre estes registos e que possibilita a rápida comutação entre ambientes de trabalho. Este ficheiro de registo é implementado como uma memória dual-port com a capacidade de 32 palavras por 32 bits por palavra.

Hardware/Software co-design para aplicações de processamento de voz 34 4.1.1.3 Unidade de Load/Store A unidade Load/Store transfere todos os dados entre os GPRs e o bus interno do CPU. Esta foi implementada como unidade de execução independente, de modo que paragens que estejam a ocorrer no subsistema de memória só afectarão a pipeline principal se apenas existir dependência entre os dados. As principais features referem-se a uma implementação exclusiva de todas as unidades de load e store apenas em hardware, buffer para os endereços de entrada, operação em pipeline e alinhamento de endereços para acessos rápidos à memória. Sempre que é necessário realizar uma instrução, quer de load, quer de store, a LSU verifica se todos os operandos estão disponíveis. Estes são os seguintes: operando com o registo do endereço, operando com o registo dos dados( para instruções de store), operando com os registos de dados do destino ( para instruções de load). 4.1.1.4 Excepções As excepções no core são geradas quando existe uma fonte a indicar que ocorreu uma situação especial, como é o caso das interrupções externas, certos acessos a memória ou erros internos como é o caso da execução de opcode de instruções que não estão presentes no Or1k, chamadas do sistema, ou ainda excepções internas como é o caso de breakpoints. Quando uma determina excepção ocorre, o controlo é transferido para um handler capaz de a atender. Assim, o Program Counter deve ser carregado com o endereço onde o handler se localiza. O tratamento destas excepções é realizado apenas no modo de supervisão. 4.1.1.5 System Unit A unidade do sistema conecta todos os outros sinais do CPU/DSP que não estão ligados directamente às interfaces de dados e de instruções. Implementa também todos os registos especiais que estão estritamente ligados com o sistema, como é o caso do registo de supervisão. 4.1.1.6 Pipeline e MAC Unit É de salientar o carácter das várias instruções inteiras que podem ser realizadas nesta unidade como é o caso das aritméticas, de comparação, lógicas, e as que realizam as rotações e shifts sobre os operandos.

Hardware/Software co-design para aplicações de processamento de voz 35 A maior parte destas instruções pode ser executada apenas num único ciclo de relógio. 4.1.1.7 DSP MAC A unidade de MAC executa operações do tipo DSP MAC, optimizadas para o cálculo do processamento do sinal. Esta unidade permite multiplicações entre operandos de 32-bits, possuindo um acumulador de 48-bits. 4.1.2 Caches É conveniente realizar a caracterização das caches. As caches usadas neste processador seguem o modelo de Harvard pois é realizada a separação entre a cache de dados e de instruções. As caches, quer de dados quer de instruções, são de uma única entrada e mapeadas directamente. Há a possibilidade de poder desactivar ou invalidar as caches através da escrita de registos especiais destinados a esta mesma função. A cache usando a configuração padrão está organizada em 512 linhas, sendo que cada linha consiste em 16 bytes de dados, bits de estado e endereço da tag. A cache é preenchida através de bursts de 16 bytes sempre que a tag e os bits correspondentes no endereço são diferentes. Neste preenchimento a palavra crítica é escrita simultaneamente na cache e direccionada para a unidade que realizou o pedido, o que minimiza as paragens devido à latência associado ao preenchimento da cache. Para o caso da cache de dados, esta permite que as tags sejam armazenadas e executa a substituição das linhas. No caso da cache de instruções, ela comunica com a unidade de fetch de forma adequada, de modo que esta última permite o cálculo do endereço efectivo. As caches quer de dados quer de instruções estão acopladas fortemente com a interface exterior para que a eficiência no acesso ao controlador de memória seja máxima.

Hardware/Software co-design para aplicações de processamento de voz 36 Word n Byte pos. Word n+1 Byte pos. Word n+2 Byte pos. Word n+3 Byte pos. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Tag Address State Bits Número de linhas que constitui a cache Figura 9: Estrutura da cache A configuração de default das caches é de 8KB, sendo possível ter valores para a sua dimensão de 1KB até 8Kyte, o que indica admitindo bursts de 16 bytes que teremos 64 e 512 linhas respectivamente. 4.1.3 Memory Management Unit O Or1k está dotado de memória virtual, realizando a separação entre dados e instruções no tratamento da memória virtual, adoptando-se uma arquitectura do tipo Harvard para a mesma. O valor para o tamanho do TLB é variável entre 16 e 256 entradas, quer para o caso de dados quer para o caso de instruções, sendo este por default de uma única entrada. O espaço de endereçamento é linear sendo que os endereços virtuais são de 32 bits e os endereços físicos de 24 a 32-bits. Existe ainda um esquema que trata da protecção das páginas. O tamanho das páginas é de 8KB estando inerente a cada uma delas os respectivos atributos. 4.1.4 Power Management Unit Uma grande potencialidade integrada neste processador é a unidade que permite a redução do consumo de potência. Esta é capaz de reduzir o consumo de 2x a 100x. É possível ainda fazer o controlo da frequência do relógio no modo de funcionamento de baixa velocidade e no modo de idle. Com esta unidade é possível activar dispositivos que estavam adormecidos, através de uma interrupção. Tem incorporado clock gating de modo que o sinal de clock só seja recebido por determinadas unidades se o sinal de controlo estiver activo.

Hardware/Software co-design para aplicações de processamento de voz 37 4.1.5 Unidade de Debug O Or1k tem incorporado uma unidade que permite a realização de debug convencional. O modo de Debug não interfere com o funcionamento do RISC e do sistema. A unidade de debug permite que se faça o supervisionamento do RISC e do sistema em tempo real. É possível ter acesso ao controlo da unidade que permite realizar o depuramento quer seja a partir do RISC quer seja a partir do interface de desenvolvimento. 4.1.6 Tick Timer Integrado O Or1k tem implementado internamente um tick timer que está ligado directamente ao relógio do RISC e que é usado pelo sistema operativo para fazer a medição precisa do tempo e agendar tarefas do sistema. O temporizador permite que se faça a contagem de 2^32 ciclos de relógio. Entre interrupções o período máximo entre estas é de 2^28. Possui ainda uma máscara que faz a configuração deste mesmo registo. O timer tem ainda como outra feature vários modos de operação: single run gerando uma única interrupção; continues timer, onde estão a ser geradas temporizações bem determinadas. É possível executar ainda o reset deste mesmo timer. 4.1.7 Programmable Interrupt Controller Este controlador de interrupções recebe interrupções externas direccionando-as para o CPU core, atribuindo-lhes níveis de prioridade. Existem duas interrupções que não podem ser desactivadas, respectivamente a 0 e a 1. No total existem 32 interrupções, estando disponíveis apenas 30 pelas razões já referidas. Estas 30 interrupções podem ser mascaradas de forma a servirem os objectivos de determinada aplicação. Deve ser mencionado que existem 3 registos associados ao PIC que fazem a sua configuração e que se englobam no conjunto de registos especiais. 4.1.8 Unidades Adicionais E Definidas Pelo Utilizador Os responsáveis pelo desenvolvimento deste mesmo processador permitiram que unidades adicionais fossem incorporadas e que fossem introduzidas como unidades standard.

Hardware/Software co-design para aplicações de processamento de voz 38 No total é possível introduzir 8 unidades que podem ser controladas através de registos especiais presentes ou instruções dedicadas. 4.1.9 Ferramentas De Suporte Ao Desenvolvimento As ferramentas que devem ser mencionadas referem os compiladores GNU ANSI C, C++, Java e Fortran. Existem ainda o GNU debugger, linker, assembler, utilities e um simulador dedicado à arquitectura. Será dedicado neste documento uma secção a estas ferramentas, pois constituem o suporte para o desenvolvimento deste mesmo projecto. 4.1.10 Sistemas Operativos Suportados: O Or1k é capaz de suportar como sistemas operativos o Linux, o UClinux, e OAR RTEMS real-time OS. 4.1.11 Interface do Sistema O protocolo de comunicação que tem incorporado designa-se Wishbone. O protocolo permite fluxo de dados bidireccional e com reduzida latência. O sistema construído com base no Or1k é ainda dotado de uma interface dual, ou seja, existe fluxo de execução de dados e de instruções simultâneo, o que, de facto, é uma enorme vantagem em cadeias de processamento exigentes. Existem ainda vários cores desenvolvidos e que podem ser ligados de forma transparente ao Or1k, como é o caso de controladores de memória, Uarts, GPIOS. 4.2 Nível de transferência lógica de registos do OR1200 Para simular e posteriormente realizar a síntese física do OpenRISC 1200 é necessário ter acesso ao código fonte descrito em Verilog. Para esta operação existem duas alternativas: - usar o comando cvs integrado em plataformas que usam o sistema operativo Linux (disponível também em emuladores de Linux para Windows), ou fazer o download através da ferramenta CvsGet presente em www.opencores.org. Para obter os ficheiros, usando o comando cvs, deve ser corrido o seguinte script: export CVSROOT=:pserver:cvs@cvs.opencores.org:/home/oc/cvs cvs z9 co or1k/or1200/

Hardware/Software co-design para aplicações de processamento de voz 39 Assim na pasta or1k/or1200 estará todo o código fonte que é necessário para realizar as operações de simulação e de síntese. Caso o método para obter o código fonte seja o CvsGet, deve na página web seguir o link CvsGet e colocar no campo que permite escolher o módulo, or1k/or1200. A figura seguinte mostra de forma mais clara como realizar esta tarefa. Figura 10 CvsGet da página www.opencores.org A revisão pode ser opcional, sendo por default obtida a mais recente das versões. Pode-se alternativamente usar o WinCvs para ter acesso a esta informação. Dos ficheiros que existem disponíveis há alguns que merecem especial atenção, como é o caso do top-level do CPU/DSP core, or1200_cpu.v, o top-level do sistema que inclui o módulo anterior, or1200_top.v e os restantes periféricos referidos na figura Figura 7 deste documento, assim como o ficheiro or1200_defines.v que contém as macros que controlam o comportamento do sistema e as features que estão implementadas. Assim, por exemplo, é possível escolher se a cache de dados está ou não implementada, e, caso esteja, qual o seu tamanho e realizar a configuração de registos que são responsáveis pelo comportamento da mesma, através das macros que estão activas.

Hardware/Software co-design para aplicações de processamento de voz 40 As macros de maior importância estão na tabela seguinte, e que mostra qual o procedimento indicado para ter um sistema que responda às necessidades das aplicações que se pretendem desenvolver. Macro Configuração do OpenRISC OR1200_ASIC Distingue se o suporte de desenvolvimento é um ASIC ou FPGA. OR1200_NO_DC Se estiver definida faz com que a cache de dados não seja implementada. OR1200_NO_IC Se estiver definida faz com que a cache de instruções não seja implementada. OR1200_NO_DMMU Se estiver definida, a MMU para dados não está presente no sistema OR1200_NO_IMMU Se estiver definida, a MMU para instruções não está presente no sistema OR1200_IC_1W_4KB Define o tamanho da cache de instruções para 4KB. OR1200_DC_1W_4KB Define o tamanho da cache de dados para 4KB. OR1200_IC_1W_8KB Define o tamanho da cache de instruções para 8KB. OR1200_DC_1W_8KB Define o tamanho da cache de dados para 8KB. OR1200_BIST Define se a memória incorporada tem autoteste incorporado OR1200_PM_IMPLEMENTED Define se a Unidade de tratamento de potência está presente no sistema. OR1200_DU_IMPLEMENTED Define se a unidade de Debug está implementada no OR1k. OR1200_PIC_IMPLEMENTED Permite implementar ou não a unidade que controlo as interrupções. OR1200_PIC_INTS Define qual o número de interrupções pode ser suportado no sistema OR1200_TT_IMPLEMENTED Define se o Tick timer está ou não presente no sistema OR1200_SB_IMPLEMENTED Define se o Store buffer está ou não presente no sistema. OR1200_MULT_IMPLEMENTED Define se a multiplicação está presente no OpenRISC 1200 OR1200_MAC_IMPLEMENTED Define se existe a unidade MAC no Sistema OR1200_QMEM_IMPLEMENTED Define se existe ou não memória interna no Or1k Tabela 8: Macros e respectiva interferência no sistema

Hardware/Software co-design para aplicações de processamento de voz 41 Aqui estão referidas as macros que são de facto aquelas que permitem a presença dos blocos que constituem o top-level do OpenRISC 1200. No entanto, para cada um dos blocos principais que estas macros controlam, existem outras macros que para o bloco referido, quando implementado fazem a sua configuração As definições relativas aos registos especiais e para os bits que os compõem, também estão presentes neste ficheiro. Consulte-se o registo UPR que, por exemplo, revela que unidades estão presentes através do valor para cada um dos seus bits. A configuração das caches é feita também recorrendo a estas macros que tratam das tags sempre presentes nestas unidades. O tratamento da memória virtual, quer para dados quer para instruções, também tem a sua configuração presente neste ficheiro. Para as restantes unidades que se evidenciam no Or1k, existem registos associados que devem ser configurados, e é aqui que deve ser feita esta mesma operação. Assim, o ficheiro OR1200_defines.v deve ser estudado com detalhe de modo a que o sistema esteja de acordo com as funcionalidades pretendidas. 4.2.1 CPU Core top-level Nesta secção pretende-se fazer uma caracterização para o RTL do processador, quer através dos módulos que o constituem, como pela funcionalidade apresentada por cada um destes. Para proceder à extracção destes mesmos resultados foi usada a ferramenta de síntese disponível no ambiente integrado ISE, para a FPGA XCV600. Foi criado então um projecto com a configuração das FPGA correcta, XCV600, obtendo-se uma hierarquia que mostrava as dependências entre módulos. Neste momento, devem ser apresentados os tamanhos que cada um dos módulos ocupa na XCV600 FPGA, incorporada na plataforma física de desenvolvimento. O esquema revela a hierarquia que está associada ao top-level do CPU core, com o tamanho descrito em slices para cada um dos sub-módulos. Os resultados apresentados foram obtidos, realizando optimização em área e com esforço elevado. Nessas condições, o tempo associado à síntese é mais elevado, ocupando a maior parte dos recursos do computador. No diagrama seguinte estão visíveis os módulos e respectivas dimensões em slices ocupadas.

Hardware/Software co-design para aplicações de processamento de voz 42 Na tabela que lhe segue estão os módulos nucleares presentes na estrutura do Core, fazendo-se a correspondência ao ficheiro onde estes se encontram definidos e a funcionalidade desempenhada no Core. Consegue-se então caracterizar o Core de acordo com estes mesmos procedimentos, o que torna muito mais fácil a visão das várias unidades que estão presentes, assim como a estrutura e hierarquia. Figura 11: Esquema que representa o Core do CPU Módulo ficheiro Função implementada no CPU CORE OR1200_ALU or1200_alu Unidade Aritmética e lógica presente no CPU OR1200_GENPC or1200_genpc Bloco associado ao fetch das instruções. Program counter e interface com a cache de instruções.

Hardware/Software co-design para aplicações de processamento de voz 43 OR1200_IF or1200_if Bloco associado ao fetch das instruções. Program counter e interface com a cache de instruções. OR1200_CTRL or1200_ctrl Usado para efectuar a descodificação das instruções. Possui ainda lógica de controlo OR1200_WBMUX or1200_wbmux CPU's write-back stage of the pipeline OR1200_GENPC or1200_genpc Program Counter. Interface com a cache de dados OR1200_RF or1200_rf Bloco que instancia as memórias usados para a transferência de dados entre registos e CPU OR1200_MULT_MAC or1200_mult_mac Unidade que está associada à ALU. Implementa a multiplicação e a operação de MAC OR1200_LSU or1200_lsu Interface entre o CPU e a cache de dados OR1200_FREEZE or1200_freeze Trata de todas as paragens e freezes que ocorrem no CPU OR1200_EXCEPT or1200_except Módulo que trata de todo as excepções que possam ocorrer no interior do CPU OR1200_OPERANDMUXES or1200_operandmuxes Multiplexer para dois operandos de leitura do ficheiro de registo OR1200_CFGR or1200_cfgr Módulo que realiza a configuração dos diversos registos (VR, UPR) OR1200_RFRAM_GENERIC Or1200_rfram_generic Memória genérica usada no register file para guardar os GPRS Tabela 9: Módulos presentes no CPU Core, correspondência com ficheiro e respectiva funcionalidade

Hardware/Software co-design para aplicações de processamento de voz 44 4.2.2 Slices associadas ao CPU Core O top-level do CPU, fica definido com base nos parâmetros anteriores, sabendo-se que, caso se pretenda implementar simplesmente o Core num dispositivo como a XCV600, são necessárias 3237 slices. Os resultados indicados direccionam-se para optimização em área, uma vez que a capacidade desta FPGA é de cerca de 7000 slices. Caso se adicionem módulos extra para fazer a comunicação com unidades externas e tornar este sistema mais dinâmico, o espaço disponível pode constituir uma séria restrição. Será bom referir que o esquema que resulta da síntese e que revela as entradas e saídas do top-level deve ser observado com especial atenção, porque é útil para compreender as interligações do CPU com os diferentes blocos. Este esquema foi obtido após a realização da síntese física para a XCV600, que é o dispositivo de interesse encontra-se no Anexo 4. 4.2.3 Top-level do OPENRISC 1200 Após se ter efectuado a caracterização do CPU core, é essencial construir um toplevel que assemble o CPU com as restantes unidades que permitem fazer um interface com um sistema externo. Como foi dito, na secção introdutória relativa ao RTL, o top-level do Or1k pode ser configurado mediante as necessidades de um determinado sistema. No entanto devemos ter acesso ao espaço que a estrutura do sistema com as features que se consideram de maior relevância. A forma mais simples de representar essa informação será apresentar a hierarquia e respectivos valores de slices ocupados por cada um dos módulos.

Hardware/Software co-design para aplicações de processamento de voz 45 Or1k Top-level 12121 slices Inst Cache(4kb) 2146 slices Data Cache (4kb) 2473 slices Debug Unit 40 slices DMMU 198 slices Tick-Timer 9 slices PIC 42 slices IMMU 150 slices Dc ram 4KB 1726 Slices Inst Tag 282 slices Tag Cache 282 slices Cache Ram ( 4KB) 1726 slices DMMU TABLE 161 slices IMMU TABLE 150 slices Tag ram 98 slices Tag ram 98 slices RAM 64x14 68 slices RAM 64x21 114 slices RAM 64x14 68 slices RAM 64x24 103 slices QMEM (8KB) Power Management Inst Wishbone interface Data wishbone interface Store Buffer CPU CORE 3237 slices 3637slices 7 slices 90 slices 44 slices 273 slices Figura 12: Organização do top-level do Or1k (slices ocupadas por cada módulo) Além de ser apresentada este mesmo esquema, irá divulgar-se uma tabela que à semelhança do que foi feito para o CPU Core contém as informações sobre os módulos, qual o ficheiro correspondente e qual a função desempenhado no sistema. Apenas considerar-se-ão os módulos nucleares. Módulo ficheiro Função implementada no CORE do OR1K OR1200_iwb_biu OR1200_iwb_biu Interface wishbone para as instruções no Core OR1200_wb_biu OR1200_wb_biu Interface wishbone para dados. OR1200_immu_top OR1200_immu_top Unidade de controlo de memória virtual para instruções OR1200_dmmu_top or1200_ctrl Unidade de controlo de memória virtual para dados OR1200_IC_TOP OR1200_IC_TOP Cache do Core para instruções. OR1200_DC_TOP OR1200_DC_TOP Cache do Core para dados. Os submódulos incluem memória para tag. OR1200_RF or1200_rf Bloco que instancia as memórias usados para a transferência de dados

Hardware/Software co-design para aplicações de processamento de voz 46 entre registos e CPU OR1200_CPU or1200_mult_mac CPU que existe no OpenRISC1200 OR1200_SB or1200_sb Store buffer OR1200_DU OR1200_PIC OR1200_TT OR1200_PM OR1200_QMEM_TOP OR1200_DU OR1200_PIC OR1200_TT OR1200_PM OR1200_QMEM_TOP Unidade que permite o debug dos programas em tempo real. Módulo que atende as interrupções que venham a ocorrer no interior do CPU ou vindas do exterior. Tick-timer do sistema. Usado para gerar interrupções periódicas. Módulo responsável pelo controlo dinâmico da potência no Or1k Memoria interna que pode ser embebida no Core e que é tem a vantagem de ter acessos mais rápidos Tabela 10: Ficheiro e funcionalidade correspondentes para um determinado módulo do Or1k No caso presente pretende-se implementar um codec de voz, que não exige qualquer memória virtual. Além deste aspecto admitiremos que a unidade de debug se encontra desactivada assim como a unidade que controla as interrupções, e tick timer. Considere-se ainda que o sistema não tem o bloco que faz o controlo inteligente de potência e que as caches, com os valores padrão referidos nas definições das macros estão activadas e que a unidade de memória virtual de dados e de instruções estão incluídas no sistema. Neste caso também se removeu a memória interna já que esta ocupava cerca de 50 % de recursos da FPGA. Caso se faça a implementação com a configuração descrita, o valor obtido na síntese para o espaço recordando que ela foi feita com optimização em área e esforço elevado, é de 7696 slices o que excede a capacidade máxima da XCV600.

Hardware/Software co-design para aplicações de processamento de voz 47 4.2.4 Adaptando o Or1k ao espaço disponível A cache de dados tem uma memória de 1024x32=4KB, e a de instruções 1024x32=4KB. Com estes valores para as caches não é possível fazer a integração do sistema na XCV600. Para solucionar este problema, fez-se a modificação das caches para 1KB. No entanto, a modificação da memória cache para 1KB implica que se faça a mudança das tags que lhe estão associadas de forma coerente. No módulo que faz a instanciação da cache e onde é invocada a memória a usar, dc_ram, é preciso que a esta seja de 256x32 = 1kB, o que indica que em termos de linha de endereço vão ser necessárias 8 linhas. Ou seja, é preciso ter uma memória com esse valor, que pode ser obtida a partir do modelo 1024x32, modificando o valor de aw, que indica o número de bits para o endereço, de 10 para 8. É necessário ainda definir a macro OR1200_IC_1W_1KB no ficheiro or1200_defines.v. Quando se faz a invocação da ram no ficheiro dc_ram.v deve-se contemplar o módulo de memória com as propriedades referidas. No entanto é preciso fazer ajustes relativos à tag que está associada à cache. Para a memória de 4KB a configuração que existe é a seguinte: 8 `ifdef OR1200_IC_1W_4KB `define OR1200_ICSIZE 12 // 4096 `define OR1200_ICINDX `OR1200_ICSIZE-2 // 10 `define OR1200_ICINDXH `OR1200_ICSIZE-1 // 11 `define OR1200_ICTAGL `OR1200_ICINDXH+1 // 12 `define OR1200_ICTAG `OR1200_ICSIZE-`OR1200_ICLS // `define OR1200_ICTAG_W 21 `endif Em primeiro lugar o tamanho da cache deve ser de 1024 o que indica a passagem da macro OR1200_ICSIZE para 10 originando os 1024 bytes. Relativamente ao número de bits da tag este terá de ser alterado para que fique de acordo com o novo valor da memória. No caso em questão, os bursts para as memórias são de 16 bytes. Para ter uma memória de 4KB como é a que esta referida é necessário que existam 4096/16 linhas, ou seja 256 blocos de 16 bytes por bloco.

Hardware/Software co-design para aplicações de processamento de voz 48 Para se ter o valor indicado de linhas devem ser usados 8 linhas de endereço. Ou seja, o bit menos significativo da tag estará na posição 12, já que do 0 ao 11 estarão ocupados pelos 4 bits que indicam o offset e pelos 8 bits que indicam qual a linha a ler da cache, ou index. A diferença entre os 32 bits do endereço e os 12 bits referidos dá o tamanho da tag. O valor que é apresentado no ficheiro defines.v é de 21, pois além dos 20 bits que resultam a partir daqui ainda é adicionado um bit para indicar a validade da tag. Isto é visível no ficheiro dc_top.v, onde ao ser invocado o módulo dc_tag, o tamanho da tag é [`OR1200_DCTAG_W-2:0], o que indica que esta corresponde a um registo de 20 bits, ou seja [21-2:0] =[19:0] o que está de acordo com o valor que apresentamos. No entanto, repare-se que é enviada para a memória que contém as tags os dados.datain({dc_addr[31:`or1200_dctagl], dctag_v), que contém os 20 bits da tag mais um que indica a validade da mesma. Assim, se agora o tamanho da cache é de 1024 bytes, então o bit menos significativo da tag, admitindo bursts de 16 bytes, estará na posição 10 do endereço. Neste caso a configuração deve ser a seguinte: `ifdef OR1200_IC_1W_1KB `define OR1200_ICSIZE 10 // 1024 `define OR1200_ICINDX `OR1200_ICSIZE-2 // 8 `define OR1200_ICINDXH `OR1200_ICSIZE-1 // 9 `define OR1200_ICTAGL `OR1200_ICINDXH+1 // 10 `define OR1200_ICTAG `OR1200_ICSIZE-`OR1200_ICLS // 6 `define OR1200_ICTAG_W 23 `endif O ajuste da tag deve ser feito também, já que neste caso a memória é de menor dimensão, devendo então considerar-se mais dois bits já que o índex diminui de dois bits tendo obviamente a tag de aumentar. Neste caso a tag será de 32-4-6=22 bits sendo que o valor do parâmetro OR1200_ICTAG_W deve incluir o bit que indica a validade de mesma. Note-se que com esta modificação no ficheiro em que se invoca a memória associada às tags deve ser adicionado um novo módulo ao projecto com as dimensões de 64x23, ou seja 64 registos de 23 bits cada, para estar de acordo com as definições referidas. Para melhor compreender os efeitos em termos de espaço ocupado que esta implementação teve, estes resultados têm de ser apresentados sob a forma de uma tabela.

Hardware/Software co-design para aplicações de processamento de voz 49 Dimensões da memória Slices Ocupadas 1024x32=4KB 1726 512x32=1KB 476 Tabela 11: Slices ocupadas por cada uma das memórias Repare-se que com o novo tamanho para as rams usadas na cache de dados e de instruções, tamanho total usado no Or1k é reduzido de forma significativa. Com esta modificação o espaço total ocupado foi reduzido de 7696 slices para 4353 slices, o que já está dentro dos limites da FPGA. A frequência máxima de funcionamento é de 17.082 MHz. Assim, temos neste momento o core do OR1k pronto para realizar a ligação com elementos adicionais, como memórias ou periféricos de outro género. De forma sumária, este modelo do Or1k apenas está implementado o Core do CPU, a cache de dados e a de instruções. Wishbone interface Inst. Cache Wishbone interface CPU/DSP CORE Wishbone interface Data Cache Wishbone interface Or1k top Figura 13: Configuração do OpenRisc 1200 no seu top-level e interface com o exterior

Hardware/Software co-design para aplicações de processamento de voz 50 O top-level em termos de diagrama encontra-se no Anexo 15 Anexo 1e mostra qual o interface do Or1k com um sistema exterior e de que modo as diversas ligações devem ser processadas. Agora que o RTL está sintetizado, pois é necessário saber qual a sua exigência em termos de espaço e embora não se tenha feito a sua simulação, ficando para uma fase posterior, devem ser apresentadas as ferramentas que nos dão acesso a uma plataforma de desenvolvimento e teste de código produzido. 4.3 Ferramentas de desenvolvimento Nesta secção pretende-se mostrar que ferramentas existem associadas a este mesmo processador, a forma de as instalar, problemas que surgiram dessa mesma tarefa, forma de utilização e revelar com um pequeno exemplo de teste como fazer o debug de programas através do código fonte gerado. Estes são os principais tópicos que devem ser apresentados nesta secção e que são fundamentais para o desenvolvimento de qualquer aplicação destinada ao OpenRisc 1200. 4.3.1 Ferramentas disponíveis e funcionalidade As ferramentas que podem ser obtidas de forma livre a partir do site http://www.opencores.org/projects/or1k/. Incluem um compilador, um debugger que permite realizar a depuração em tempo real e um simulador que tente modelar a arquitectura do processador e testar a validade do código fonte produzido. As ferramentas que até ao momento têm sido utilizadas direccionam-se para o compilador e simulador. A última destas deve ser configurada de acordo com a organização do sistema e deve existir coerência entre o compilador e secções que estão evidenciadas no ficheiro de configuração do simulador. No programa de teste essa coerência irá ser referida no sentido de mostrar de forma clara como deve ser executado esse mesmo procedimento. 4.3.2 Instalação das ferramentas

Hardware/Software co-design para aplicações de processamento de voz 51 A instalação das ferramentas de simulação, tal como acontece com a obtenção do código fonte para o RTL tem a possibilidade de ser obtido usando o comando cvs, ou fazendo o download através do CvsGet. O procedimento para o caso do CvsGet é equivalente ao que foi referido neste documento sendo que agora que os pacotes a serem obtidos devem ser or1k/binutils or1k/gcc-3.4.2, or1k/gdb-5.3 e or1k/or1ksim. No caso de ser usado o comando cvs basta indicar as directorias referidas. Em qualquer dos casos, depois de se terem estes ficheiros nas pastas respectivas é necessário executar um conjunto de instruções para a instalação e que deve ser de acordo com uma ordem bem determinada. Convém salientar que no arranque deste trabalho, estava a ser usado um emulador de Linux para Windows, Cygwin. Por essa mesma razão devem ser descritos todos os acontecimentos relativos à utilização deste mesmo software. De acordo com a indicação dos Opencores, a primeira das ferramentas a ser instalada devem ser as binutils, que contêm comandos como objdump, readelf, orientados para a arquitectura do OpenRisc, posteriormente realizar a instalação do gdb e do gcc e por último a instalação do or1ksim, já que este vai necessitar do compilador para criar os binários usados na simulação. Após termos o código fonte para instalar as binutils devem ser corridas as seguintes instruções na shell (foi feita sempre em super user.) mkdir b-b cd b-b../binutils/configure --target=or32-uclinux --prefix=/opt/or32-uclinux make all install export PATH=/opt/or32-uclinux/bin:$PATH cd.. Inicialmente não se estava a conseguir realizar esta tarefa pois é necessário o automake 1.6 e o autoconf 1.4, de modo a se proceder coma execução desta instalação. A versão do bison e do flex para versões mais recentes pode causar também problemas na instalação. Isto será tema após se observar o que decorreu com o cygwin. A instalação destas utilities foi bem sucedida, pelo que se iniciou a do compilador. As directivas para realizar a sua instalação são as seguintes: mkdir b-gcc cd b-gcc../gcc-3.4.2/configure --target=or32-uclinux --prefix=/opt/or32-uclinux \

Hardware/Software co-design para aplicações de processamento de voz 52 --local-prefix=/opt/or32-uclinux/or32-uclinux --with-gnu-as \ --with-gnu-ld --verbose --enable-languages=c make all install cd.. Também foi possível instalar este compilador dedicado e desta vez não foram obtidos erros. Resta-nos ter o gdb a funcionar assim como o simulador deste processador. Para se poder instalar o gdb os comandos a invocar na shell são: mkdir b-gdb cd b-gdb../gdb-5.3/configure --target=or32-uclinux make all cp gdb/gdb /opt/or32-uclinux/bin/or32-uclinux-gdb cd.. A instalação desta ferramenta decorreu com sucesso. A instalação do simulador, deve ser realizada através das seguintes directivas: cd or1k/or1ksim./configure --target=or32-uclinux --prefix=/opt/or32-uclinux make all install cd../.. A instalação do or1ksim originou erros e por mais tentativas que se fizessem, existiam problemas permanentes na instalação. Experimentaram-se várias versões do automake e autoconf, analisaram-se ficheiros associados à produção de makefiles, mas o erro permanecia. Enviou-se um e-mail para um dos responsáveis do projecto com os logs da configuração e da instalação, e a resposta obtida foi de que a tentativa da instalação do simular no ambiente Cygwin não havia sido testada por ele. No entanto pela consulta de fóruns associados, o problema relativo a esta instalação era comum a muitos dos posts colocados sobre esse assunto. Estando neste impasse e perdendo bastante tempo nesta secção, a solução adoptada foi usar o sistema operativo Fedora Core 2.

Hardware/Software co-design para aplicações de processamento de voz 53 Quando se fez a instalação das ferramentas indicadas, descobriu-se na página um ATS automatic self test, que testava cada uma das ferramentas, o uclinux e ainda a uclibc, mostrando em log files os resultados dessa instalação. Assim corremos esse script numa shell e a instalação foi automática, colocando as ferramentas todas em funcionamento. No decorrer do trabalho, por razão desconhecida, o acesso ao sistema operativo indicado falhou, havendo só a hipótese de recuperar o trabalho desenvolvido até ao momento, tendo sido necessário executar uma nova instalação do sistema operativo. Colocou-se em funcionamento o novo sistema operativo em funcionamento, mas pelo facto de se terem realizado alguns updates, as ferramentas correndo o script automático estavam a falhar. Pensando que o problema estaria no S.O. utilizado, colocou-se em funcionamento o Debian, mas, igualmente, ocorreu insucesso. Após a análise dos erros verificou-se que algumas das versões de software necessárias para se avançar com a instalação, estavam demasiado avançadas. Como exemplo temos o flex, que deve ser substituído pelo flex old. Também existiam ferramentas que não estavam presentes e que tiveram de ser instaladas, como é o caso do bison. Na instalação ainda houve um erro, que invocava problemas com o malloc, mas que depois de se ter feito uma busca no google deste erro se encontrou a solução a adoptar. É assim essencial verificar se as versões das ferramentas do sistema suportam ou não a instalação da GNU TOOLCHAIN, e, caso a instalação não esteja a ter sucesso, devem ser alteradas para as versões mais antigas, que podem ser obtidas a partir do manager de pacotes de instalação presente neste mesmo sistema operativo. 4.3.3 Produção de binários e ferramentas auxiliares Quando se realiza a instalação do or1ksim, são gerados os binários a partir do código fonte. Estes binários são usados no simulador, pelo que devemos ter em consideração a forma como são gerados. Para ter acesso às ferramentas a partir de qualquer directoria de trabalho deve ser chamado o seguinte comando que associa o local onde se encontram as ferramentas a uma variável de ambiente: export PATH=/opt/or32-uclinux/bin:$PATH

Hardware/Software co-design para aplicações de processamento de voz 54 Para aceder a esta mesma informação, deve ser feita a análise do makefile que se encontra na pasta or1ksim/testbench/ e que mostra como são gerados os binários que o simulador usa. O makefile é de facto extenso, e a sua análise ocupou bastante tempo, para que se pudessem encontrar as directivas de compilação para criar os ficheiros binários. Os ficheiros fonte têm de ser compilados com o or32-uclinux-gcc, o compilador dedicado a esta arquitectura, mas não se deve fazer o link indicado pela flag-c e não se deve fazer uso da standard library, -nostdlib e deve ser usada mhard-div que é uma flag dedicada para este mesmo processador. Para melhor compreensão deve ser consultado o manual do gcc. Após se ter feito a compilação de todos os ficheiros objecto deve ser feito o linking entre estes. Para isso, tem de estar presente o ficheiro default.ld que possui a divisão das várias secções de memória que estão associadas ao binário. Deve ainda ser associada a libraria libsupport.a que contém funções especiais e o objecto das excepções, except.o que é essencial para colocar o processador com a sequência de boot e o tratamento de excepções habilitado. O ficheiro except.o está presente na pasta or1ksim/testbench e foi gerado no momento da instalação a partir do except.s, ficheiro este que está escrito em assembly e que faz o tratamento das excepções. É conveniente referir como é feita a organização da memória no binário que foi produzido, para mais tarde compreender o código que foi gerado e a forma como este se organiza. MEMORY { except : ORIGIN = 0x00000000, LENGTH = 0x00002000 flash : ORIGIN = 0xf0000000, LENGTH = 0x10000000 ram : ORIGIN = 0x00002000, LENGTH = 0x001fe000 SECTIONS {.text : { *(.text) *(.rodata*) _src_beg =.;

Hardware/Software co-design para aplicações de processamento de voz 55 > flash.dummy ALIGN(0x4): { _src_beg =.; > flash.except : AT ( ADDR (.dummy)) { _except_beg =.; *(.except) _except_end =.; > except.data : AT ( ADDR (.dummy) + SIZEOF (.except)) { _dst_beg =.; *(.data) _dst_end =.; > ram.bss : { *(.bss) > ram.stack ALIGN(0x10) (NOLOAD): { *(.stack) _ram_end =.; > ram Ficheiro de apoio 1: default.ld- contém as definições das diversas regiões de memória e endereços iniciais Assim como se pode ver a parte da memória destinada a programa, flash, inicia-se em 0xF000_0000 e tem uma dimensão de 0x1000_0000. A secção das excepções compreende-se entre 0x0000_0000 e 0x0000_1FFF. A região de dados, ram, inicia-se em 0x0000_2000 e tem um tamanho de 0x001F_E000. Dentro da região de dados podemos fazer a divisão em data, bss e stack. É este ficheiro que tem ser usado para adaptar o Core ao sistema de memória que está presente na interface. E mediante os requisitos de memória, deve ser feita a modificação de endereços e de espaços associados para cada um dos diferentes segmentos definidos.

Hardware/Software co-design para aplicações de processamento de voz 56 O ficheiro default.ld que se apresentou é aquele que no makefile de estudo é utilizado para fazer o linking entre os diferentes objectos. Para se poder mostrar de forma clara como realizar esta tarefa, apresenta-se um simples makefile. A finalidade deste ficheiro é simplesmente testar a multiplicação. Repare-se que no or1ksim existem vários binários que pretendem testar vários componentes presentes no OR1k. O ficheiro fonte que estamos a usar, mul.c, encontra-se em or1ksim/testbench servindo para produzir um binário que serve de teste no simulador. Se os resultados obtidos no simulador, com binário gerado usando o makefile seguinte for igual aos obtidos a partir do binário do or1ksim/testbench/, então estamos a ter o procedimento correcto para criar esses mesmos binários para simulação. objects=mul.o except.o CC=or32-uclinux-gcc FLAGS_2=-Wall -g -nostdlib -mhard-div FLAGS_1=-I. -I/home/pedro/tools/or1k/or1ksim/testbench/support SUPPORTLIBS=/home/pedro/tools/or1k/or1ksim/testbench/support/libsupport.a LD_FLAGS= -nostdlib $(SUPPORTLIBS) mul : $(objects) $(CC) -o mul -T.//default.ld $(LD_FLAGS) $(objects) $(LD_FLAGS) mul.o : mul.c $(CC) -c $(FLAGS_1) $(FLAGS_2) mul.c Ficheiro de apoio 2:Makefile que permite a criação do binário a ser usado para a simulação Como em qualquer plataforma de desenvolvimento é fundamental conhecer a forma como é traduzido o código C em termos das instruções dedicadas para o processador, assim como as várias secções de memória e respectivo tamanho. Por essa razão são indicadas as ferramentas usadas, assim como o procedimento de utilização e os resultados esperados sempre que são invocadas. Nesse caso, podem então ser salientadas as ferramentas or32-uclinux-objdump chamada com a flag S de modo a misturar o código em C e as instruções que foram geradas com o respectivo endereço. O comando or32-uclinux-readelf com a flag a, ao ser invocado permite que se possam visualizar as diferentes secções em que o binário está dividido e o respectivo endereço inicial e o tamanho da secção que lhe está associada. Existe ainda o comando or32-uclinux-size que indica de forma sucinta os valores em termos de espaço ocupados por cada um destes.

Hardware/Software co-design para aplicações de processamento de voz 57 Por esta mesma razão apresentar-se-á aqui os resultados da invocação destes mesmos comandos e as próprias secções de memória que foram definidas no ficheiro default.ld. mul: file format elf32-or32 Disassembly of section.text: f0000000 <_reset_vector-0x100>:... f0000100 <_reset_vector>: f0000100: 9c 40 00 00 l.addi r2,r0,0x0 f0000104: 9c 60 00 00 l.addi r3,r0,0x0 f0000108: 9c 80 00 00 l.addi r4,r0,0x0 f000010c: 9c a0 00 00 l.addi r5,r0,0x0 f0000110: 9c c0 00 00 l.addi r6,r0,0x0 f0000114: 9c e0 00 00 l.addi r7,r0,0x0 f0000118: 9d 00 00 00 l.addi r8,r0,0x0 int main () { f00006d8: 9c 21 ff d4 l.addi r1,r1,0xffffffd4 f00006dc: d4 01 10 10 l.sw 0x10(r1),r2 f00006e0: 9c 41 00 2c l.addi r2,r1,0x2c f00006e4: d4 01 48 0c l.sw 0xc(r1),r9 unsigned t1; unsigned t2; unsigned t3; printf ("%08x\n", MACRC); f00006e8: 04 00 00 9b l.jal f0000954 <_macrc> f00006ec: 15 00 00 00 l.nop 0x0 f00006f0: d4 01 58 04 l.sw 0x4(r1),r11 f00006f4: 18 60 f0 00 l.movhi r3,0xf000 f00006f8: a8 63 10 94 l.ori r3,r3,0x1094 f00006fc: d4 01 18 00 l.sw 0x0(r1),r3 f0000700: 04 00 00 bc l.jal f00009f0 <_printf> f0000704: 15 00 00 00 l.nop 0x0 MAC (888888887, 0x87654321); f0000708: 18 80 34 fb l.movhi r4,0x34fb f000070c: a8 84 5e 37 l.ori r4,r4,0x5e37 f0000710: 18 60 87 65 l.movhi r3,0x8765 f0000714: a8 63 43 21 l.ori r3,r3,0x4321 f0000718: c4 04 18 01 l.mac r4,r3 Disassembly of section.except: 00000000 <_except_beg>: 0: 9c 21 ff 88 l.addi r1,r1,0xffffff88 4: d4 01 48 1c l.sw 0x1c(r1),r9 8: d4 01 50 20 l.sw 0x20(r1),r10 c: 19 20 f0 00 l.movhi r9,0xf000 10: a9 29 02 a8 l.ori r9,r9,0x2a8 14: 19 40 00 00 l.movhi r10,0x0 18: a9 4a 20 00 l.ori r10,r10,0x2000 1c: 44 00 48 00 l.jr r9

Hardware/Software co-design para aplicações de processamento de voz 58 20: 15 00 00 00 l.nop 0x0 00000024 <_buserr_vector_end>: 24: 9c 21 ff 88 l.addi r1,r1,0xffffff88 28: d4 01 48 1c l.sw 0x1c(r1),r9 2c: d4 01 50 20 l.sw 0x20(r1),r10 30: 19 20 f0 00 l.movhi r9,0xf000 34: a9 29 02 a8 l.ori r9,r9,0x2a8 38: 19 40 00 00 l.movhi r10,0x0 3c: a9 4a 20 04 l.ori r10,r10,0x2004 40: 44 00 48 00 l.jr r9 44: 15 00 00 00 l.nop 0x0 Figura 14: Resultados obtidos ao ser invocado o or32-uclinux-objdump S mul ELF Header: Magic: 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, big endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: OpenRISC Version: 0x1 Entry point address: 0xf0000000 Start of program headers: 52 (bytes into file) Start of section headers: 46920 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 4 Size of section headers: 40 (bytes) Number of section headers: 17 Section header string table index: 14 Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1].text PROGBITS f0000000 004000 0010ce 00 AX 0 0 4 [ 2].rel.text REL 00000000 00c518 000000 08 15 1 4 [ 3].dummy PROGBITS f00010d0 008034 000000 00 W 0 0 1 [ 4].except PROGBITS 00000000 006000 0001d4 00 AX 0 0 1 [ 5].rel.except REL 00000000 00c518 000000 08 15 4 4 [ 6].data PROGBITS 00002000 008000 000034 00 WA 0 0 4 [ 7].rel.data REL 00000000 00c518 000000 08 15 6 4 [ 8].bss NOBITS 00002040 002040 000100 00 WA 0 0 16 [ 9].stack PROGBITS 00002140 008034 001000 00 0 0 1

Hardware/Software co-design para aplicações de processamento de voz 59 [10].stab PROGBITS 00000000 009034 001260 0c 12 0 4 [11].rel.stab REL 00000000 00c518 000000 08 15 a 4 [12].stabstr STRTAB 00000000 00a294 001414 00 0 0 1 [13].comment PROGBITS 00000000 00b6a8 000036 00 0 0 1 [14].shstrtab STRTAB 00000000 00b6de 00006a 00 0 0 1 [15].symtab SYMTAB 00000000 00b9f0 000710 10 16 49 4 [16].strtab STRTAB 00000000 00c100 000416 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific) Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x00000000 0x00000000 0x02040 0x02140 RW 0x2000 LOAD 0x004000 0xf0000000 0xf0000000 0x010ce 0x010ce R E 0x2000 LOAD 0x006000 0x00000000 0xf00010d0 0x001d4 0x001d4 R E 0x2000 LOAD 0x008000 0x00002000 0xf00012a4 0x00034 0x00034 RW 0x2000 Section to Segment mapping: Segment Sections... 00.except.data.bss 01.text 02.except 03.data There is no dynamic segment in this file. There are no relocations in this file. There are no unwind sections in this file. Symbol table '.symtab' contains 113 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 FILE LOCAL DEFAULT ABS except.s 2: 00000000 0 FILE LOCAL DEFAULT ABS board.h 3: 00000000 0 FILE LOCAL DEFAULT ABS except.s 4: 00000000 0 FILE LOCAL DEFAULT ABS support/spr_defs.h 5: 00000000 0 FILE LOCAL DEFAULT ABS except.s 6: 00000000 0 FILE LOCAL DEFAULT ABS <command line> 7: 00000000 0 FILE LOCAL DEFAULT ABS <built-in> 8: 00000000 0 FILE LOCAL DEFAULT ABS except.s 9: f0000000 0 SECTION LOCAL DEFAULT 1 10: 00002000 0 SECTION LOCAL DEFAULT 6 11: 00002040 0 SECTION LOCAL DEFAULT 8 12: 00002140 0 SECTION LOCAL DEFAULT 9 13: 00003140 0 NOTYPE LOCAL DEFAULT 9 _stack 14: 00000000 0 SECTION LOCAL DEFAULT 4

Hardware/Software co-design para aplicações de processamento de voz 60 15: 00000000 0 NOTYPE LOCAL DEFAULT 4 _buserr_vector 16: f00002a8 0 NOTYPE LOCAL DEFAULT 1 store_regs 17: 00000024 0 NOTYPE LOCAL DEFAULT 4 _buserr_vector_end 18: 00000024 0 NOTYPE LOCAL DEFAULT 4 _dpfault_vector 19: 00000048 0 NOTYPE LOCAL DEFAULT 4 _dpfault_vector_end 20: 00000048 0 NOTYPE LOCAL DEFAULT 4 _ipfault_vector 21: 0000006c 0 NOTYPE LOCAL DEFAULT 4 _ipfault_vector_end 22: 0000006c 0 NOTYPE LOCAL DEFAULT 4 _lpint_vector 23: 00000090 0 NOTYPE LOCAL DEFAULT 4 _lpint_vector_end 24: 00000090 0 NOTYPE LOCAL DEFAULT 4 _align_vector 25: 000000b4 0 NOTYPE LOCAL DEFAULT 4 _align_vector_end 26: 000000b4 0 NOTYPE LOCAL DEFAULT 4 _illinsn_vector 27: 000000d8 0 NOTYPE LOCAL DEFAULT 4 _illinsn_vector_end 28: 000000d8 0 NOTYPE LOCAL DEFAULT 4 _hpint_vector 29: 000000fc 0 NOTYPE LOCAL DEFAULT 4 _hpint_vector_end 30: 000000fc 0 NOTYPE LOCAL DEFAULT 4 _dtlbmiss_vector 31: 00000120 0 NOTYPE LOCAL DEFAULT 4 Figura 15:Resultados obtidos ao ser invocado o or32-uclinux-readelf a mul Ainda exista a possibilidade de se visualizar o hexdump da secção pretendida. Para isso deve ser invocado o comando or32-uclinux-readelf x 1 mul ( representa o número de secção) Hex dump of section '.text': 0xf0000000 00000000 00000000 00000000 00000000... 0xf0000010 00000000 00000000 00000000 00000000... 0xf0000020 00000000 00000000 00000000 00000000... 0xf0000030 00000000 00000000 00000000 00000000... 0xf0000040 00000000 00000000 00000000 00000000... 0xf0000050 00000000 00000000 00000000 00000000... 0xf0000060 00000000 00000000 00000000 00000000... 0xf0000070 00000000 00000000 00000000 00000000... 0xf0000080 00000000 00000000 00000000 00000000... 0xf0000090 00000000 00000000 00000000 00000000... 0xf00000a0 00000000 00000000 00000000 00000000... 0xf00000b0 00000000 00000000 00000000 00000000... 0xf00000c0 00000000 00000000 00000000 00000000... 0xf00000d0 00000000 00000000 00000000 00000000... 0xf00000e0 00000000 00000000 00000000 00000000... 0xf00000f0 00000000 00000000 00000000 00000000... 0xf0000100 9c400000 9c600000 9c800000 9ca00000.@...`... 0xf0000110 9cc00000 9ce00000 9d000000 9d200000..... 0xf0000120 9d400000 9d600000 9d800000 9da00000.@...`... 0xf0000130 9dc00000 9de00000 9e000000 9e200000..... 0xf0000140 9e400000 9e600000 9e800000 9ea00000.@...`... 0xf0000150 9ec00000 9ee00000 9f000000 9f200000..... 0xf0000160 9f400000 9f600000 9f800000 9fa00000.@...`... 0xf0000170 9fc00000 9fe00000 1860f000 a8630188...`...c.. 0xf0000180 44001800 15000000 0400002b 15000000 D...+... 0xf0000190 18200000 a8213140 1860f000 a86310d0....!1@.`...c.. Continua.. Figura 16: Resultados da execução do comando or32-uclinux-readelf x 1 mul

Hardware/Software co-design para aplicações de processamento de voz 61 text data bss dec hex filename 0x12a2 0x34 0x100 5078 13d6 mul Figura 17:Resultados obtidos ao ser invocado o or32-uclinux-size -- radix=16 mul ( formatação em hexadecimal) Agora que se revelaram o método para a produção dos binários usados para a simulação, resta demonstrar que configuração deve estar presente no simulador, e como esta deve ser feita de forma coerente com o ficheiro default.ld. Tem que existir concordância entre estes caso contrário o simulador não funcionará. 4.3.4 Simulação dos binários produzidos Na fase inicial de teste do simulador com os binários que haviam sido produzido, o simulador não executava nada do pretendido. Isto relacionava-se com o facto de não se estar a fazer o linking com o ficheiro das excepções, que contém a inicialização do sistema e o atendimento das excepções. Após se ter feito essa operação já com o objecto das excepções considerado, o simulador estava a produzir os resultados esperados para o código que se tinha escrito. O simulador em questão é invocado através de or32-uclinux-sim, sendo preciso para que este corra com determinadas configurações que exista o ficheiro sim.cfg, no directório em que este se encontra. Caso não exista esse ficheiro, ele irá correr com um ficheiro padrão que não corresponde à configuração que queremos para um determinado sistema. Para que o simulador corresse quando o invocávamos era preciso que o fizéssemos com permissões de root. Existem dois modos para ser invocado este simulador, um modo em que este executa continuamente e outro em que pode estar em funcionamento interactivo. Para que o segundo modo esteja disponível deve ser chamado com a flag i. Assim para correr o simulador deve ser chamado o comando da seguinte forma or32-uclinux-sim sim.cfg i mul.

Hardware/Software co-design para aplicações de processamento de voz 62 Relativamente ao simulador no modo interactivo, apresenta-se em baixo um snapshot do mesmo. Irá referir-se ainda um conjunto de comandos que pode ser usado para facilitar o debug de programas e que auxiliam na tarefa árdua da descoberta da causa do erro. Reading script file from 'sim.cfg'... Verbose on, simdebug off, interactive prompt on Machine initialization... Clock cycle: 100ns Data cache present. Insn cache tag present. BPB simulation off. BTIC simulation off. Building automata... done, num uncovered: 0/212. Parsing operands data... done. loadcode: filename main startaddr=00000000 virtphy_transl=00000000 Not COFF file format ELF type: 0x0002 ELF machine: 0x8472 ELF version: 0x00000001 ELF sec = 17 Section:.text, vaddr: 0xf0000000, paddr: 0xf0000000, offset: 0x000000c0, size: 0x000033ac Section:.except, vaddr: 0x00000000, paddr: 0xf00033ac, offset: 0x0000346c, size: 0x000001d4 Section:.data, vaddr: 0x00002000, paddr: 0xf0003580, offset: 0x00003640, size: 0x0000003c WARNING: dependstats stats must be enabled to do history analisis. WARNING: Unable to open RX file stream. Resetting memory controller. Resetting Tick Timer. Resetting Power Management. Resetting PIC. Starting at 0x00000000 Exception 0x00000100 (Reset) at 0x00000000, EA: 0x00000000, ppc: 0x00000000, npc: 0x00000004, dpc: 0x00000000, cycles 0, #0 (sim) t 00000104: : 9c600000 l.addi r3,r0,0x0 (executed) [cycle 20, #2] 00000108: : 9c800000 l.addi r4,r0,0x0 (next insn) GPR00: 00000000 GPR01: 00000000 GPR02: 00000000 GPR03: 00000000 GPR04: 00000000 GPR05: 00000000 GPR06: 00000000 GPR07: 00000000 GPR08: 00000000 GPR09: 00000000 GPR10: 00000000 GPR11: 00000000 GPR12: 00000000 GPR13: 00000000 GPR14: 00000000 GPR15: 00000000 GPR16: 00000000 GPR17: 00000000 GPR18: 00000000 GPR19: 00000000 GPR20: 00000000 GPR21: 00000000 GPR22: 00000000 GPR23: 00000000 GPR24: 00000000 GPR25: 00000000 GPR26: 00000000 GPR27: 00000000 GPR28: 00000000 GPR29: 00000000 GPR30: 00000000 GPR31: 00000000 flag: 0 (sim) Ficheiro de apoio 3: Aspecto do simulador. São evidenciados os registos e as instruções do processador.

Hardware/Software co-design para aplicações de processamento de voz 63 Os comandos principais que podem ser usados para realizar debug são os que estão na tabela. Coloca-se um exemplo da sua utilização e a sua funcionalidade dos mesmos. run Comando q r t ex; run 10; run 10 hush Funcionalidade no simulador Abandonar o simulador Mostra os registos GPRS Executa a próxima instrução Executa, de acordo com o exemplo, as 10 próximas instruções. No primeiro caso faz o display dos registos para o ecrã em todas as instruções executadas e no segundo caso já não o faz. pr <r> <value> ex: pr 20 12 Coloca o registo 20 com o valor 12 dm endereço_inicial endereço_final Ex: dm 0x0000_2000 0x0000_4000 pm endereço valor pm 0x0000_1234 12 pc valor info cm endereço_inicial endereço_final size cm 0x0000_1234 0x0000_4567 0xf break endereço (ou label) break 0xF000_1FAB break teste breaks reset Stat Mostra o conteúdo da posição de memória 0x0000_2000 à posição 0x0000_4000 Coloca na posição de memória 0x0000_1234 o valor 12 Coloca o programa counter como valor indicado. Isto significa que a próxima instrução a ser executada será aquela que está no endereço indicado pelo valor que foi carregado para este mesmo registo Permite ter acesso aos valores que os diferentes registos especiais têm definidos e observar se por exemplo alguma flag foi ou não activa ou que unidades estão presentes. Copia um bloco com o tamanho de 0xf que se inicia no endereço 0x0000_1234, para outra zona de memória iniciando a cópia a partir de 0x0000_4567 Coloca quando se está a realizar o debug um breakpoint no endereço indicado, ou numa label associada a um determinado endereço. Mostra que breakpoints é que estão presentes na simulação. Executa o reset do simulador, voltando-se ao início da memória do programa e com os registos limpos. Permite visualizar as estatísticas associadas

Hardware/Software co-design para aplicações de processamento de voz 64 às instruções que foram executadas Permite ter acesso à ajuda incorporada no help simulador. Permite ver que instruções foram hist executadas até ao momento actual. Tabela 12:Comandos disponíveis no simulador e respectiva funcionalidade Para ter o simulador configurado de acordo com o pretendido é preciso ter o ficheiro sim.cfg de acordo. Na parte inicial deste mesmo ficheiro de configuração estão presentes os dispositivos de memória que têm de estar de acordo com default.ld quer em termos de endereços iniciais quer em termos de espaço. Assim, se observarmos o ficheiro default.ld e o segmento inicial do sim.cfg, que será mostrado seguidamente veremos que estes concordam. /* MEMORY SECTION This section specifies how the memory is generated and the blocks it consists of. type = random/unknown/pattern Specifies the initial memory values. 'random' generates random memory using seed 'random_seed'. 'pattern' fills memory with 'pattern'. 'unknown' does not specify how memory should be generated, leaving the memory in a undefined state. This is the fastest option. random_seed = <value> random seed for randomizer, used if type = 'random'. pattern = <value> pattern to fill memory, used if type = 'pattern'. nmemories = <value> number of memory instances connected instance specific: baseaddr = <hex_value> memory start address size = <hex_value> memory size name = "<string>" memory block name ce = <value> chip enable index of the memory instance delayr = <value>

Hardware/Software co-design para aplicações de processamento de voz 65 reading cycles, required for read access, -1 if instance does not support delayw = <value> cycles, required for write access, -1 if instance does not support writing log = "<filename>" filename, where to log memory accesses to, no log, if log command is not specified */ section memory /*random_seed = 12345 type = random*/ pattern = 0x00 type = unknown /* Fastest */ nmemories =2 device 0 name = "FLASH" ce = 0 baseaddr = 0xf0000000 size = 0x00800000 delayr = 10 delayw = 2 enddevice device 1 name = "RAM" ce = 1 baseaddr = 0x00000000 size = 0x00210000 delayr = 1 delayw = 2 enddevice Figura 18: Configuração da memória usada no simulador Aqui estão definidos dois dispositivos de memória, sendo que o endereço inicial da flash e o que existe no ficheiro default.ld são os mesmos e o espaço que aqui está definido satisfaz aquele que é definido no ficheiro default.ld. O somatório da memória dedicada para as excepções e aquela que está direccionada para bss, stack e dados está perfeitamente contido no espaço indicado e o valor inicial para o endereço também se encontram em concordância. É necessário referir as várias secções que estão presentes neste ficheiro, sendo que cada um dos componentes instanciados no sim.cfg tem informação detalhada constituindo assim a única documentação que existe para o funcionamento deste simulador.

Hardware/Software co-design para aplicações de processamento de voz 66 Neste documento não é pretendido fazer uma descrição exaustiva sobre esse aspecto mas referir que elementos existem e se estão ou não habilitados na configuração presente. SECÇÃO MEMORY IMMU DMMU IC DC SIM VAPI CPU PM BPB DEBUG MC UART DMA ETHERNET GPIO VGA Descrição/Activação Define como a memória é gerada e em que blocos está dividida. Secção que trata da configuração da Immu. No caso presente está inactiva. Secção que trata da configuração da Dmmu. No caso presente está inactiva. Secção que faz a configuração da cache de instruções e dos seus parâmetros como tamanho e numero de sets presentes. Activa na configuração presente. Secção que faz a configuração da cache de dados e dos seus parâmetros como tamanho e numero de sets presentes. Activa na configuração presente. Nesta secção é configurado o comportamento do simulador relativamente à profundidade das mensagens de debug, activação da criação de histórico e estatísticas de execução, assim como se inibe a geração de logs relativos à execução das instruções. Secção que faz a configuração da Verification API, usada para verificação avançada do Core. Esta desabilitada na configuração actual. Esta secção permite definir vários parâmetros para o CPU, como a versão eo registo UPR. Permite activar ou não a unidade que faz o controlo de potência. No presente caso esta encontra-se desactivada. Nesta secção controlam-se os parâmetros relativos à previsão dos saltos. Agora encontra-se desactivada. Secção destinada configurar o comportamento da unidade de debug que está incorporada no Or1k. Permite realizar a configuração do controlador de memória, como o endereço base. No momento presente este encontra-se activo. Permite a configuração da UART, como o seu endereço no espaço de memória, o tipo de UART que se está a usar e ainda algumas configurações temporais. Está desactivada para a presente configuração. Permite a configuração do controlador de acesso a memória, como o endereço ocupado. Não está activo. Permite a configuração da ethernet no sistema. Mais uma vez está indicado o seu endereço no espaço total. Não esta activada. Atenção apenas foi possível arrancar com o simulador desactivando a ethernet que vinha activada no ficheiro inicial. Faz a configuração dos general purpose input outputs. Endereço base e o número da interrupção. Não se encontra presente na configuração actual. Permite que se configure um controlador de VGA ou de LCD. Não está

Hardware/Software co-design para aplicações de processamento de voz 67 activada na configuração pretendida. Secção que trata da configuração do gerador de ticks e qual a interrupção TICK TIMER que está associada ao mesmo. Está desabilitado no presente momento. Secção que faz a configuração do frame buffer. Mais uma vez o endereço FRAME a que este responde também é um dos parâmetros a configurar. Está BUFFER inactivo. Secção que realiza a configuração do teclado. O endereço e o número da KBD interrupção têm de ser controlados. Faz a configuração do controlador ARA/ATAPI. O endereço é ATA configurável assim como o número da interrupção. Permite simular um disco, ou usar o disco do sistema local. Não se encontra activado. Configura a unidade de compilação do OpenRisc. Encontra-se desactivada CUC na presente situação. Tabela 13: Secções que constituem o sim.cfg e que podem sofrer alterações Após ter-se revelado estes detalhes sobre o ficheiro de configuração, é então esclarecedor mostrar o simulador a correr em qualquer um dos modos que foram referidos e observar os resultados. No modo contínuo apenas teremos acesso aos valores finais, obtidos por mensagens na consola, enquanto que no segundo caso poderemos ir observando a evolução do programa em termos de registos e de instruções e valores que estão na memória. Apresentamos aqui o resultado da simulação deste mesmo binário na figura seguinte, tendo como base a função que estamos a tentar testar. Exception 0x00000100 (Reset) at 0x00000000, EA: 0x00000000, ppc: 0x00000000, npc: 0x00000004, dpc: 0x00000000, cycles 0, #0 [34fb5e37,87654321] (e70a2588,cac28a17) [34fb5e37,87654321] (e70a2588,cac28a17) [70a2588c,00000003] (00000001,51e709a4) [70a2588c,00000005] (00000003,8512c460) [70a2588c,00000007] (00000006,99833034)...... [8f504efc,00000071] (3d5e252e,41e3c5e8) [9e54b724,000003e5] (3d5e23b1,e1d1051c) report(0xdeaddead); exit(00000000) @reset : cycles 0, insn #0 @exit : cycles 173401, insn #16125 diff : cycles 173401, insn #16125 Figura 19:Resultado da execução do programa de teste mul

Hardware/Software co-design para aplicações de processamento de voz 68 O programa correu sem qualquer problema obtendo-se os resultados que estão a observar na figura que se encontra acima. O que aqui está a ser mostrado é a multiplicação de dois valores, na coluna da esquerda, e o resultado do acumulador após esta operação. Está apresentada a parte mais significativa MACHI e a menos significativa MACLO. Na secção seguinte existe um conjunto de funções que se pensa importante destacar e que estão incluídas na libraria libsupport.a. Convém referir que a informação disponibilizada pelo simulador inclui o número de instruções executadas e ciclos necessários. A diferença entre eles prende-se com os valores usados no delay para leitura e escrita nas memórias. 4.3.5 Funções de interesse presentes na libraria usada no linking Nesta libraria encontram-se algumas das funções que auxiliam o debug. Estas funções podem ser encontradas na directoria or1k/or1ksim/testbench/support, nos ficheiros support.c e support.h. Das que lá se encontram a função printf era a de maior interesse. Esta foi implementada directamente para este simulador, mas nos testes que foram executados não produziu qualquer resultado, o que de facto é estranho. Foram várias as tentativas com programas simples, que mesmo um elementar printf, como sendo o conteúdo de todo o ficheiro, não produzia o que tanto era esperado. Por este motivo as funções report, mfspr, mtspr são de especial interesse. A função report permite que o valor de uma determinada variável seja enviado para o écran como report ( variável). As outras duas funções permitem fazer a leitura e a escrita de registos especiais definidos em spr_defs.h. Conjugando a função report e a função mfspr, temos a possibilidade de realizar a leitura e colocar o valor no ecrã desse mesmo registo, o que é uma forma de debug quer para estes registos, (flags accionadas p ex.) ou para variáveis que estão a ser usadas no programa. Existem outras funções como é o caso da de exit, start_timer, read_timer que são úteis. A função exit permite terminar a execução de um determinado programa, retornando o valor de erro que foi gerado. As restantes tal como o seu nome indica, associam-se com o arranque e a leitura de temporizadores.

Hardware/Software co-design para aplicações de processamento de voz 69 Coloca-se aqui um exemplo do uso das funções de report, e de escrita e de leitura de valores especiais e o respectivo resultado da execução das mesmas. #include "spr_defs.h" #include "support.h" int main (void) { unsigned long x; mtspr(spr_machi, 0x55); x = mfspr (SPR_MACHI); report (x); exit (5); Ficheiro de apoio 4: Ficheiro que mostra a potencialidade das funções referidas Section:.text, vaddr: 0xf0000000, paddr: 0xf0000000, offset: 0x00004000, size: 0x00000b14 Section:.except, vaddr: 0x00000000, paddr: 0xf0000b14, offset: 0x00006000, size: 0x000001d4 Section:.data, vaddr: 0x00002000, paddr: 0xf0000ce8, offset: 0x00008000, size: 0x00000034 WARNING: dependstats stats must be enabled to do history analisis. setting status register BSY 0x80 WARNING: Unable to open RX file stream. UART at 0x90000000 Resetting memory controller. Resetting Tick Timer. Resetting Power Management. Resetting PIC. Starting at 0x00000000 Exception 0x00000100 (Reset) at 0x00000000, EA: 0x00000000, ppc: 0x00000000, npc: 0x00000004, dpc: 0x00000000, cycles 0,#0 report(0x00000055); exit(00000005) @reset : cycles 0, insn #0 @exit : cycles 16398, insn #1479 diff : cycles 16398, insn #1479 Figura 20: Resultado da simulação do ficheiro representado acima Como é visualizado, a utilidade destas funções é fundamental para diagnosticar qualquer erro que esteja a acontecer e a partir dá fazer a correcção necessária.

Hardware/Software co-design para aplicações de processamento de voz 70 5 Profiling e análise do codec AMR 5.1 Introdução Nesta fase do trabalho procedeu-se à realização da análise e profiling do codec AMR. A análise concentrou-se no AMR a funcionar no seu modo de maior débito: 12.2 kbit/s. A norma define, na sua versão em código C, uma série de contadores que vão sendo incrementados de forma a se conseguir medir o peso computacional do codec. Estes contadores estão associados a funções que definem operações básicas do codec como adições subtracções, multiplicações, etc. Desta forma é-nos possível estimar o peso computacional em WMOPS (weighted million operations per second). Regra geral, é necessário multiplicar-se este valor por 1.2 a 1.5 para se obter um valor em MIPS (million instructions per second). 5.2 Análise ao estilo de programação definido pela norma Fazendo uma análise visual ao código encontra-se rapidamente um conjunto de instruções, ditas básicas, que são responsáveis por todas as operações aritméticas existentes no código. Ou seja, ao longo de todo o código não existe uma única chamada aos operadores do C de +, *, /, etc. exceptuando as necessárias a controlar ciclos (for, while). for (i = 5; i > 0; i--) { f1[i] = L_add (f1[i], f1[i - 1]); move32 (); /* f1[i] += f1[i-1]; */ f2[i] = L_sub (f2[i], f2[i - 1]); move32 (); /* f2[i] -= f2[i-1]; */ a[0] = 4096; move16 (); for (i = 1, j = 10; i <= 5; i++, j--) { t0 = L_add (f1[i], f2[i]); /* f1[i] + f2[i] */ a[i] = extract_l (L_shr_r (t0, 13)); move16 (); t0 = L_sub (f1[i], f2[i]); /* f1[i] - f2[i] */ a[j] = extract_l (L_shr_r (t0, 13)); move16 (); Este pequeno exemplo ilustra o que foi dito. Repare-se que para se para se realizar uma soma recorre-se à função L_add e não ao operador +. Outras funções que aparecem exaustivamente no código são as funções move16(), move32(), test(), logic16() e logic32(). Estas últimas funções encontram-se definidas no ficheiro count.c, e servem para

Hardware/Software co-design para aplicações de processamento de voz 71 incrementar um contador para se realizar o profiling do código caso se pretenda tal operação. Quanto à implementação dos algoritmos propriamente ditos concluiu-se que o código já se encontra optimizado no sentido em que não existem funções que estejam implementadas de forma não eficiente. 5.2.1 Operações básicas De seguida é apresentada uma tabela com todas as operações básicas definidas pela norma do codec. Estas instruções podem ser encontradas nos ficheiros basic_op.h e basicop2.c. Neste último é feita uma descrição detalhada de todas estas operações. De notar que Word16 e Word32 significa uma variável de 16 e 32 bits respectivamente. No lado esquerdo da tabela é mostrado o peso computacional que cada operação tem. Este valor é depois usado para se realizar o profiling. Instrução Descrição Peso Word16 add (Word16 var1, Word16 var2) Short add 1 Word16 sub (Word16 var1, Word16 var2) Short sub 1 Word16 abs_s (Word16 var1) Short abs 1 Word16 shl (Word16 var1, Word16 var2) Short shift left 1 Word16 shr (Word16 var1, Word16 var2) Short shift right 1 Word16 mult (Word16 var1, Word16 var2) Short mult 1 Word32 L_mult (Word16 var1, Word16 var2) Long mult 1 Word16 negate (Word16 var1) Short negate 1 Word16 extract_h (Word32 L_var1) Extract high 1 Word16 extract_l (Word32 L_var1) Extract low 1 Word16 round (Word32 L_var1) Round 1 Word32 L_mac (Word32 L_var3, Word16 var1, Word16 var2) Mac 1 Word32 L_msu (Word32 L_var3, Word16 var1, Word16 var2) Msu 1 Word32 L_macNs (Word32 L_var3, Word16 var1, Word16 var2) Mac without sat 1 Word32 L_msuNs (Word32 L_var3, Word16 var1, Word16 var2) Msu without sat 1 Word32 L_add (Word32 L_var1, Word32 L_var2) Long add 2 Word32 L_sub (Word32 L_var1, Word32 L_var2) Long sub 2 Word32 L_add_c (Word32 L_var1, Word32 L_var2) Long add with c 2 Word32 L_sub_c (Word32 L_var1, Word32 L_var2) Long sub with c 2 Word32 L_negate (Word32 L_var1) Long negate 2 Word16 mult_r (Word16 var1, Word16 var2) Mult with round 2 Word32 L_shl (Word32 L_var1, Word16 var2) Long shift left 2 Word32 L_shr (Word32 L_var1, Word16 var2) Long shift right 2 Word16 shr_r (Word16 var1, Word16 var2) Shift right with round 2 Word16 mac_r (Word32 L_var3, Word16 var1, Word16 var2) Mac with rounding 2 Word16 msu_r (Word32 L_var3, Word16 var1, Word16 var2) Msu with rounding 2 Word32 L_deposit_h (Word16 var1) 16 bit var1 -> MSB 2 Word32 L_deposit_l (Word16 var1) 16 bit var1 -> LSB 2 Word32 L_shr_r (Word32 L_var1, Word16 var2) Long shift right with 3

Hardware/Software co-design para aplicações de processamento de voz 72 round Word32 L_abs (Word32 L_var1) Long abs 3 Word32 L_sat (Word32 L_var1) Long saturation 4 Word16 norm_s (Word16 var1) Short norm 15 Word16 div_s (Word16 var1, Word16 var2) Short division 18 Word16 norm_l (Word32 L_var1) Long norm 30 Tabela 14: Operações básicas usadas pela norma do codec AMR. 5.3 Uso do ficheiro count.c para realizar o profiling do codec Neste ficheiro encontra-se uma série de funções dedicadas à avaliação do peso computacional no codec. De seguida são apresentadas as funções que interessam sobre o ponto de vista de quem quer realizar tal avaliação ao codec. Para isto ser feito é necessário criar-se um contador recorrendo à função getcounterid. Esta função retorna um inteiro que identifica o número do contador, e recebe como parâmetro uma string que posteriormente pode ser usada pelo programa para mostrar e identificar ao utilizador o contador em questão. Se não houver mais contadores disponíveis a função retornará 0. Depois de ser criado o contador é necessário inicializá-lo. Para tal devemos, antes de mais, seleccionar o contador. Isto é conseguido recorrendo à função setcounter, que deve receber como parâmetro a variável que o identifica. De seguida, podemos então chamar a função Init_WMOPS_counter. Esta função não recebe qualquer parâmetro. Uma vez tendo o contador pronto a operar, para o colocar em funcionamento (contagem) basta seleccioná-lo com setcounter. A partir deste ponto do código, a contagem associada é então feita até que um outro contador seja seleccionado ou o programa termine. Para se poder visualizar os resultados medidos pelos contadores devemos recorrer à função WMOPS_output, devendo estar seleccionado o contador pretendido. O profiling feito por estas funções é realizado tendo em conta o tempo necessário para se processar 1 frame de sinal (20 ms). Por esta razão sempre que se processa 1 frame na sua totalidade deve-se chamar a função Reset_WMOPS_counter. Esta função coloca os seus contadores a zero e atribui um peso de 0.00005 à contagem realizada até aí, ou seja, 1/20ms * 10^6. Por esta razão, é fundamental chamar-se sempre esta função quando se processa uma frame, caso contrário os resultados finais vêm errados. Nos resultados finais mostrados pela função WMOPS_output, vêm, por exemplo, da seguinte forma:

Hardware/Software co-design para aplicações de processamento de voz 73 encoder:wmops=12.154 Average=12.125 WorstCase=12.176 WorstWC=12.274 (425 frames) Primeiro vem identificada a parte do codec onde foi feito o profiling, no caso encoder, de seguida aparece, em WMOPS, a contagem realizada na última frame processada (WMOPS=12.154). É apresentada a média aritmética realizada ao longo da simulação (Average=12.125). É ainda apresentado o pior caso verificado na simulação de uma frame (WorstCase=12.176) e o pior dos piores casos (WorstWC=12.274). Este último valor é avaliado tendo-se em conta uma subdivisão em várias subpartes da porção do algoritmo a fazer o profiling e considerando-se as piores medições realizadas nessas subpartes. Para se proceder a esta subdivisão basta recorrer-se à função fwc() nos locais pretendidos do algoritmo. 5.4 Profiling do codec AMR A realização do profiling do codec incidiu sobre o seu funcionamento a 12.2 kbit/s Para se preparar o código C para a realização do profiling ao codec recorreu-se aos ficheiros c onde é despoletado todo o processamento do codec. Estes ficheiros são coder.c e decoder.c respectivamente para codificar e descodificar. Aqui foi adicionado ao início do código a inicialização dos contadores pretendidos, como explicado na secção anterior. No final do mesmo código foi feito o printf dos resultados. No início do processamento de cada frame de sinal é chamada a função Reset_WMOPS_counter. Para se seleccionar os contadores para as partes do código a realizar o profiling, foi-se aos ficheiros sp_enc.c e cod_amr.c para o codificador e sp_dec.c e dec_amr.c seleccionando-se aí os contadores pretendidos nas respectivas partes do algoritmo do codec. As variáveis inteiras que guardam as identificações dos diversos contadores foram declaradas no ficheiro typedef.h. Este ficheiro aparece incluído por todos os ficheiros c, podendo-se assim usar livremente as variáveis usadas que identificam os contadores em qualquer parte do código. Para se compilar o programa em modo de profiling é necessário invocar o makefile da seguinte forma: make MODE=WMOPS

Hardware/Software co-design para aplicações de processamento de voz 74 Primeiramente realizaram-se dois testes para o codificador e descodificador: um sem o modo dtx (discontinuous transmission) e o outro com este modo. Os resultados obtidos foram os seguintes: Average WorstCase WorstWorstCase Codificador sem dtx 12,125 12,176 12,274 com dtx 9,12 12,521 - Descodificador sem dtx 1,854 1,942 1,957 com dtx 1,624 1,942 - Tabela 15: Peso computacional do codec AMR em WMOPS (weighted million operations per second). Da última tabela é possível concluir-se que, em média, estando o codec a funcionar em modo descontínuo (dtx), o peso computacional é muito mais baixo do que aquele obtido no pior caso (WorstCase). Tal facto já era de esperar uma vez que codificar frames de silêncio é mais fácil do que frames onde exista voz, fazendo então com que a média medida baixe. Na prática, o codec AMR funciona sempre com o modo dtx, no entanto para se fazer uma estimativa da velocidade de relógio de um CPU é necessário considerar-se o pior caso, sendo este aquele onde há codificação de voz. Isto é equivalente a correr o codec sem o modo dtx, ou seja, o sinal a codificar é sempre considerado como sendo sinal de voz. Tal facto pode ser comprovado pela tabela anterior, onde o pior caso em modo dtx é idêntico ao caso médio sem o modo dtx. Por estas razões, ou seja, estando todo o peso computacional na codificação de voz propriamente dita, vamos concentrar-nos no codec sem o seu modo dtx. Da tabela também se pode inferir acerca do desvio de padrão que eventualmente pode sofrer o peso computacional do codec. Verifica-se aqui que os piores casos e o caso médio lidos sem o modo dtx são idênticos. Isto significa que de uma forma geral o codec não irá ter farmes pontuais onde, por algum motivo, o peso computacional aumente consideravelmente. De seguida, codificador e descodificador foram divididos em diversas partes de forma a se medir o peso computacional de cada uma. Podem verificar-se as partes do codec que poderão limitar o seu funcionamento em tempo real numa implementação prática. Obteve-se as seguintes tabelas:

Hardware/Software co-design para aplicações de processamento de voz 75 Average WorstCase WorstWC Pré-processamento 0,198 0,200 0,200 1,63% Cálculo LPC 0,612 0,614 0,614 5,05% Quantização LSP 2,134 2,163 2,163 17,60% Pitch em malha aberta 1,520 1,546 1,546 12,54% Pitch em malha fechada 2,958 2,971 2,974 24,40% Codebook inovativo 4,212 4,246 4,286 34,74% Quantização de ganhos 0,144 0,150 0,152 1,19% Act. da memória,... 0,233 0,235 0,235 1,92% Outros... 0,104 0,104 0,104 0,86% Total do codificador 12,125 12,176 12,274 Tabela 16: Peso computacional do codificador de voz AMR no modo 12.2 kbit/s em WMOPS. Average WorstCase WorstWC LSF para LPC 0,127 0,129 0,129 6,85% Codebook adaptativo 0,221 0,223 0,223 11,92% Codebook inovativo 0,038 0,050 0,053 2,05% Descodificação ganhos 0,038 0,040 0,040 2,05% Construção da excitação 0,287 0,289 0,289 15,48% Filtro de síntese 0,188 0,241 0,241 10,14% Pós-filtragem 0,496 0,498 0,498 26,75% Pós-processamento 0,214 0,216 0,216 11,54% Outros... 0,306 0,346 0,346 16,50% Toatal do descodificador 1,854 1,942 1,957 Tabela 17: Peso computacional do descodificador de voz AMR no modo 12.2 kbit/s em WMOPS. Como já se tinha verificado anteriormente o codificador tem um peso computacional superior ao descodificador (mais de 6 vezes). Concentrando-nos no codificador, a pesquisa do codebook inovativo é, sem dúvida alguma a responsável pela grande maioria do processamento do codificador e, claro está, de todo o codec de voz AMR. Cerca de 35% do processamento realizado mas frames que originam o maior peso computacional ao codificador, é proveniente de uma pesquisa bastante intensiva do codebook inovativo. Esta é a parte do codec onde deve ser feito um esforço grande para ser optimizada. 5.5 Optimização do algoritmo pesquisa do codebook inovativo no modo 12.2 kbit/s Na pesquisa do codebook inovativo (ou algébrico) do codificador AMR, uma subframe de sinal (40 amostras) é dividida em algumas pistas e cada pulso, que irá constituir o vector final do codebook, é localizado de acordo com uma estratégia nessas

Hardware/Software co-design para aplicações de processamento de voz 76 pistas por forma a se obter uma eficiente modelização do sinal de excitação para uma subframe. No modo 12.2 kbit/s, as 40 posições de cada subframe são divididas em 5 pistas onde cada pista irá conter dois pulsos como mostra a tabela seguinte: Pistas Pulsos Posições 1 i0, i5 0, 5, 10, 15, 20, 25, 30, 35 2 i1, i6 1, 6, 11, 16, 21, 26, 31, 36 3 i2, i7 2, 7, 12, 17, 22, 27, 32, 37 4 i3, i8 3, 8, 13, 18, 23, 28, 33, 38 5 i4, i9 4, 9, 14, 19, 24, 29, 34, 39 Tabela 18: Posições potenciais dos pulsos individuais no codebook algébrico para o modo 12.2 kbit/s. Como resultado, cada vector de excitação terá 10 pulsos com amplitudes +1 ou -1 (daí o nome algébrico por vezes dado ao codebook). O algoritmo de pesquisa do codebook começa por seleccionar o máximo global do sinal filtrado anterior, escolhe um máximo local de uma pista na subframe e de seguida começa a realizar pesquisas exaustivas sobre todas as possibilidades existentes em duas pistas consecutivas (8*8=64 possibilidades) e selecciona daqui dois pulsos, um para cada pista, que garantam o melhor sinal de excitação possível de acordo com um critério de minimização dum erro. De seguida, passa para as outras duas pistas seguintes e faz o mesmo. Isto é feito 4 vezes de forma a termos 8 pulsos. Os outros 2 pulsos já tinham sido seleccionados no início do algoritmo. Isto origina um total de 8*8*4=256 possibilidades de pulsos a pesquisar. O algoritmo pega de seguida noutro máximo local duma pista e faz exactamente o mesmo. Este processo repete-se no total 4 vezes para os 4 máximos locais das 4 diferentes pistas (a pista que tem o máximo global não é considerada). Sendo assim, temos 256*4=1024 pesquisas a realizar. O que foi feito para se diminuir o peso computacional desta parte do codec foi, simplesmente, efectuar um maior número de conjuntos de pesquisas mas sendo estas por si só menos extensas que as originais. Ou seja, se imaginarmos uma pesquisa em árvore, aumentamos a profundidade da pesquisa ao longo da árvore mas cada nó terá um peso computacional inferior. Na pesquisa realizada pelo algoritmo da norma do codec considerou-se então pesquisas não a duas pistas em conjunto mas sim a uma só pista isoladamente,

Hardware/Software co-design para aplicações de processamento de voz 77 S/N (db) 2 1 0-1 -2-3 -4 seleccionando apenas um pulso. Daqui resulta uma pesquisa de 8 diferentes possibilidades em vez das anteriores 64 (8*8). Claro está que agora teremos que pesquisar 8 vezes (pistas) até termos os 8 pulsos escolhidos. O resto do algoritmo mantém-se inalterado. Assim sendo, passamos agora a ter um total de 8*8*4=256 pesquisas a realizar em vez das 1024 anteriores, ou seja, uma redução de 4 vezes no número de pesquisas. Correndo agora o programa com as alterações realizadas, verifica-se um novo peso computacional de 10.522 WMOPS em média para este modo. Ou seja, um decréscimo de 15.2% em todo o codec em relação ao original. Fazendo-se estas alterações é agora necessário tentar-se medir a degradação que é produzida no novo sinal de saída. Tratando-se este codec dum codec de voz, a avaliação da degradação do sinal de saída deve ser feita qualitativamente e não quantitativamente, ou seja, é preciso ter-se em linha de conta que a qualidade dum codec de voz é avaliada pelo ouvido duma pessoa. Como não podemos avaliar o codec qualitativamente, pois não dispomos de meios nem da experiência necessária para realizar tal avaliação, tentemos avaliá-lo quantitativamente. Sendo assim, mediu-se a relação sinal ruído do codec. O sinal considerou-se o sinal de entrada e o ruído a diferença entre o sinal de entrada e o de saída. Um dos problemas que logo à partida surge é a questão do atraso entre o sinal de entrada e o de saída. O que se fez então, foi traçar um gráfico com a relação sinal ruído em função de diversos atrasos. Obteve-se os seguintes gáficos: Relação sinal ruído do codec original modificado S/N (db) 2 1 0-1 -2-3 -4-5 Relação sinal ruído do codec original modificado S/N (db) 5 4 3 2 1 0-1 -2-3 -4 Relação sinal ruído do codec original modificado -5 0 20 40 60 80 Atraso da saída em relação à entrada -6 0 20 40 60 80 Atraso da saída em relação à entrada -5 0 20 40 60 80 Atraso da saída em relação à entrada

Hardware/Software co-design para aplicações de processamento de voz 78 Nestes três gráficos analisou-se três ficheiros de áudio distintos. O primeiro é constituído por um sinal de voz masculino e feminino; o segundo por um sinal masculino e o terceiro por um sinal de voz feminino. É logo notório que os gráficos do atraso da saída em relação à entrada em função da relação sinal ruído entre a versão do algoritmo original e modificado praticamente se sobrepõem. Isto é um bom indicador de que a modificação introduzida ao codec não alterou a qualidade do sinal. Outro aspecto que é importante de realçar é que nos três gráficos apresentados o atraso que o descodificador introduz (pico máximo dos gráficos) não é exactamente o mesmo. Isto pode ser explicado tendo em vista que o codec de voz é um sistema não linear, ou seja, os atrasos introduzidos para diferentes frequências podem ser distintos. Note-se que este facto não significa que o codec poderá ter um desempenho menos bom, pois, tratando-se dum codec de voz, ou seja, sinais tipicamente com poucas frequências distintas. Daqui pode-se concluir que tentar quantificar a degradação introduzida ao codec com esta alteração num valor concreto não é correcto. Se o tentássemos fazer estaríamos a chegar a valores que apenas seriam válidos para aquele bocado de sinal de fala o que não é representativo da qualidade em geral. Apenas podemos, então, ouvir algumas sequências de sinais de voz que foram descodificadas e compará-las com as obtidas originalmente. Aqui, não é perceptível qualquer diferença entre os sinais que ouvimos pelo que podemos concluir que as melhorias introduzidas para reduzir o peso computacional não se repercutem numa redução na qualidade do sinal de saída do codec. As alterações realizadas ao código podem ser consultadas no Anexo 1. 5.6 Medição das operações básicas no codec Para se poder ter uma ideia da quantidade de vezes que uma determinada operação básica é chamada ao longo do codec a funcionar no seu modo de 12.2 kbit/s, procedeu-se à alteração do ficheiro count.c para se realizar tal medição. Aqui, incluem-se mais contadores, para além dos já existentes, capazes de contabilizar o número de chamadas a uma determinada operação básica.

Hardware/Software co-design para aplicações de processamento de voz 79 Os resultados obtidos são apresentados na tabela seguinte e incluem valores do codificador mais descodificador a correr no modo 12.2 kbit/s sem dtx. Os valores absolutos dos números de ocorrências são relativos a uma frame (160 amostras). Instrução Nº ocorrências Percentagem L_mac 85673 35,03% DataMove16 36967 15,11% L_msu 20634 8,44% mult 18228 7,45% L_mult 14798 6,05% round 12101 4,95% add 9406 3,85% Test 9321 3,81% L_shl 8241 3,37% sub 8064 3,30% extract_h 5614 2,30% DataMove32 3137 1,28% L_shr 2463 1,01% extract_l 2161 0,88% L_sub 1998 0,82% shr 1361 0,56% Logic16 1018 0,42% L_add 840 0,34% shl 691 0,28% mult_r 480 0,20% abs_s 270 0,11% L_deposit_h 260 0,11% L_abs 200 0,08% negate 180 0,07% norm_l 158 0,06% L_shr_r 108 0,04% norm_s 100 0,04% div_s 98 0,04% L_negate 10 0,00% L_deposit_l 6 0,00% L_macNs 0 0,00% L_msuNs 0 0,00% L_add_c 0 0,00% L_sub_c 0 0,00% shr_r 0 0,00% shift_r 0 0,00% mac_r 0 0,00% msu_r 0 0,00% L_shift_r 0 0,00% L_sat 0 0,00% Logic32 0 0,00% Tabela 19: Número de ocorrências médias das operações básicas do codec no modo 12.2 kbit/s por frame. Desta tabela verifica-se que as operações de mac (multiplicar e acumular) representam cerca de 43.5% (L_mac mais L_msu) do total das operações básicas realizadas no codec. As alterações realizadas sobre o ficheiro count.h encontram-se no Anexo 2.

Hardware/Software co-design para aplicações de processamento de voz 80 6 Pequeno programa a simular no processador lei A 6.1 Introdução A finalidade da implementação da lei A é testar o processador com um programa simples. 6.2 A lei A A Lei A é uma lei que se criou capaz de passar de forma eficiente um sinal de voz codificado em PCM de 13 bits para outro de apenas 8 bits. Isto é conseguido tendo-se em consideração as características típicas dum sinal de voz quanto à sua amplitude. Sabe-se que existe uma predominância de sinais de baixa amplitude e este facto é aproveitado por esta lei, que utiliza um maior número de bits do sinal original para a sua passagem para 8 bits quando este tem baixa amplitude. Para sinais de maiores amplitudes é feito o contrário. Na tabela seguinte pode-se ver, de uma forma simples, como deve ser codificada a saída de acordo com a lei A. Na tabela são apresentados valores de entrada sem o seu bit mais significativo e o mesmo é feito no sinal codificado. A lei A define que o bit mais significativo da entrada deve ser negado em relação ao bit mais significativo de saída. Esta lei é definida pela recomendação G.711 da ITU-T. Sinal entrada PCM Saída codificação 0000000wxyza 000wxyz 0000001wxyza 001wxyz 000001wxyzab 010wxyz 00001wxyzabc 011wxyz 0001wxyzabcd 100wxyz 001wxyzabcde 101wxyz 01wxyzabcdef 110wxyz 1wxyzabcdefg 111wxyz Tabela 20: Codificação definida pela lei A.

Hardware/Software co-design para aplicações de processamento de voz 81 6.3 Implementação da lei A no processador No Anexo 3 pode ser vista a implementação feita em código C da lei A para ser simulado pelo nosso processador. Como o objectivo desta parte do trabalho é executar um programa de teste para nos ajudar na implementação do codec AMR, tentou-se que a estrutura do código fosse o mais semelhante à do AMR. Ou seja, aqui fez-se também processamento dos dados sobre um conjunto de amostras (160 amostras) como no codec AMR. Para se testar o correcto funcionamento do programa no simulador OR1k construiuse um ficheiro com sequências de testes de entrada e saída do codificador e descodificador da lei A. Estas sequências de teste foram previamente obtidas do site da 3GPP. Desta forma, construiu-se um ficheiro (um header file) onde se declararam estes vectores em C que depois podem ser comparados com os obtidos pela simulação. O programa começa a processar os dados frame por frame e em cada frame os dados obtidos são verificados se estão correctos. Em caso de erro o programa deve parar e sair com exit (-1). Se correr tudo bem, o programa sai no fim com exit (0). Durante o processamento de cada frame é feito o report do número da frame que foi processada. Para criar o binário para ser usado na simulação basta correr o makefile destinado ao mesmo. Neste makefile devem ser indicadas as dependências entre os ficheiros e realizada a sua compilação tendo esse aspecto em consideração. Para gerar o binário, os ficheiros objectos devem ser o a_law.o, encoder.o e o ficheiro das excepções, except.o. As flags usadas na compilação e no linking estão bem evidenciadas. Após uma simulação, os resultados obtidos foram os seguintes: report(0x00000000); report(0x00000001); report(0x00000002); report(0x00000003); report(0x00000004); report(0x00000005); report(0x00000006); report(0x00000007); report(0x00000008); report(0x00000009); report(0x0000000a); report(0x0000000b); exit(00000000) @reset : cycles 0, insn #0 @exit : cycles 6323149, insn #576437 diff : cycles 6323149, insn #576437

Hardware/Software co-design para aplicações de processamento de voz 82 Como se pode verificar a simulação correu bem. Foram processadas 12 frames de áudio e programa saiu com exit (0). Daqui concluímos que o programa da lei A está correctamente implementado e que tanto o simulador como o compilador fornecidos pelo site da opencores funcionam correctamente.

Hardware/Software co-design para aplicações de processamento de voz 83 7 O caminho para a implementação do Or1k em hardware 7.1 Plataforma de desenvolvimento A plataforma de desenvolvimento para o teste do sistema codificador/ descodificador é a carta FEUPCI, que dispõe de duas FPGAs a XCV600 e XC400, uma memória de 128k x 32 posições da cypress, CY7C1340A, Gerador de relógio cy22150 e módulos desenvolvidos para o DAC HI1171 (8bits, 40Msps) e o conversor ADC AD6644 de 14 bits a 65 MHz. A FPGA XCV600 é aquela que dispõe de maior espaço disponível e por essa mesma razão é que foi utilizada. As limitações de espaço foram motivo para que na síntese, se tivesse que realizar a diminuição do tamanho das caches e remoção da memória interna do processador. No entanto como foi referido, após essas modificações, o espaço ocupado passa a ser de 4353 slices, o que já se encontra de acordo com as especificações pretendidas em termos de espaço. A memória que existe na placa deve ser capaz de suportar com o programa desenvolvido e além desta, a região de dados para o codificador/descodificador. Os conversores AD e DA que estão disponíveis permitem, numa fase posterior de teste, verificar se a codificação, realizando possivelmente testes em tempo real, está correcta e se a descodificação também se executa de forma correcta. 7.2 Comunicação entre a memória e o Or1k Em qualquer sistema envolvendo um processador, a comunicação entre dispositivos de armazenamento é nuclear. E este caso não é excepção, pelo que o interface entre estes deve ser apresentado de forma clara. De acordo com as características do Or1k em termos de comunicação com o exterior, foi visto que este dispunha do protocolo wishbone. Isso é visível no esquema do top-level do Or1k relativamente à parte de dados quer de instruções. Ora, o que acontece relativamente ao sistema actual é que a memória da cypress, não apresenta qualquer interface Wishbone. Assim é obrigatório integrar esse protocolo, o

Hardware/Software co-design para aplicações de processamento de voz 84 que pode ser feito pela construção de um módulo que implemente a comunicação com base nessa linguagem entre o Or1k e a memória externa. CPU/DSP CORE Wishbone interface Wishbone interface Inst. Cache Data Cache Wishbone interface Wishbone interface Dispositivo que permite o acesso do Or1k à memoria através do protocolo Wishbone DATA Control Cypress CY7C1340A Or1k top Figura 21: Interface necessário para realizar a comunicação com a memória da Cypress Assim, para solucionar essa questão, consultaram-se os cores que existem na página http://www.opencores.org/projects.cgi, e procuraram-se aqueles que suportavam o protocolo wishbone e se relacionavam directamente com memórias. O core que respondia às necessidades referidas era um controlador de memórias capaz de disponibilizar a comunicação usando o protocolo Wishbone e suportando SDRAMS, SSRAMS como é o caso da memória da Cypress e ASRAMS. Este módulo pode ser encontrado em http://www.opencores.org/projects.cgi/web/mem_ctrl/overview, estando disponibilizada a informação essencial sobre este, assim como o código fonte descrito em verilog. 7.3 Controlador de Memórias O controlador de memórias de facto surge como o elemento capaz de efectuar a ligação entre o Or1k e a memória da Cypress integrada na plataforma de desenvolvimento. A vantagem da utilização deste módulo é sem dúvida a flexibilidade que este tem para se conectar a dispositivos de armazenamento, num total até 8, ocupando cada um, regiões de memória distintas e sendo a sua configuração uma tarefa relativamente fácil de ser executada.

Hardware/Software co-design para aplicações de processamento de voz 85 Como o controlador de memórias pode servir para realizar a ligação para diferentes tipos de memória, este possui um controlo temporal flexível orientado para o tipo de memória ligado a cada um dos chips select. Por outro lado, este controlador possui a capacidade de realizar transferências do tipo burst, desde que a memória seja capaz de responder a este tipo de operação. Devem ser reveladas outras potencialidades deste core como é a possibilidade de ter uma sequência de arranque padrão, geração de bits de paridade e o teste dos mesmos e ainda a possibilidade de funcionamento em modo power down. No entanto, a potencialidade mais interessante do ponto de vista da sua integração no sistema que se pretende desenvolver, é a interface Wishbone com o processador Or1k, sendo por esse mesmo motivo que se deseja integrar este módulo para construir a ponte de comunicação com o exterior. Assim poderemos ver o controlador na seguinte perspectiva: Figura 22: Modelo pretendido em relação ao controlador de memórias Modelo de um master que comunica em wishbone Wishbone Memory Controller Data Addr Contro Ram A arquitectura deste controlador está descrita de forma detalhada no manual que se encontra na página referida e onde se descarregou o código fonte. Em qualquer dos casos julga-se necessário apresentar a figura que tenta sintetizar os blocos fundamentais inerentes a este dispositivo.

Hardware/Software co-design para aplicações de processamento de voz 86 Figura 23:Arquitectura do controlador de memórias Do ponto de vista do utilizador, da arquitectura apresentada, interessa a forma como o controlador tem as suas interfaces, nomeadamente a que faz a comunicação com periféricos que necessitam de acesso a dados de memória, e a interface destinado à comunicação de memórias. Além destes aspectos, deve ser realçado o facto de existirem no controlador dois relógios. Um deles corresponde ao relógio principal que está associado ao protocolo wishbone. O outro é obtido a partir do anterior tendo metade da sua frequência, tendo o cuidado da sincronização deste, sendo destinado à memória presente no sistema. 7.3.1 Área ocupada pelo controlador de memórias É claro que este controlador tem de ser integrado no sistema que se pretende implementar, de tal forma que a sua caracterização em termos de slices ocupadas, considerando as mesmas condições para a realização da síntese.

Hardware/Software co-design para aplicações de processamento de voz 87 De acordo com o ISE o valor encontrada para a área deste controlador é de 890 slices, pelo que adicionada à área do top level do or1k originará um sistema com 5243 slices, que está dentro dos limites da XCV600. 7.3.2 Configuração do controlador de memórias Nesta secção pretende-se mostrar como se faz a configuração deste controlador, através dos registos que estão associados a um determinado dispositivo. Iremos realizar a configuração de uma memória SSRAM, que tem equivalência à da Cypress, estando este a ocupar uma determinada localização no espaço de endereçamento. Deve ter-se em mente que a consulta ao Anexo 16, clarifica a compreensão ao longo da explicitação. Uma vez que os endereços usados no protocolo Wishbone são de 32 bits, consideremos que o endereço inicial para a memória será 32 h1000_0000. Para iniciar a configuração a primeira tarefa é definir a BA_MASK, base address mask, que indica qual o tamanho disponível para cada Chip Select. Esta máscara é aplicada aos bits 21 até 28 do endereço. A máscara a usar para o endereço pode ser 8 b1000_0000. Para activar o chip select associado a uma determinada memória deve ser feito o and entre o bit 28 e 21 do endereço base da memória e a máscara, e também o and entre os bits 28 ao 21 do endereço que se pretende aceder. A documentação relativa à configuração do registo não indica isto com clareza. Após se terem feito as operações referidas, os resultados das mesmas devem ser comparados, e em caso de igualdade o chip select será activado para a memória correspondente. Esta configuração tem de ser feita no Chip select configuration register, pelo que seguidamente se detalha esse registo. Os bits 23:16 contêm o endereço base de cada memória, estando a informação descrita na documentação não totalmente correcta. A forma como se deve fazer já foi referida e será em termos de código descrito do género: assign cs_d = ((csc[23:16] & csc_mask[7:0]) == (addr[28:21] & csc_mask[7:0])) & csc[0];

Hardware/Software co-design para aplicações de processamento de voz 88 A operação descrita está de acordo com o que foi afirmado, pelo que o código é sempre a fonte para retirar qualquer incongruência que se esteja a cometer na documentação apresentada. Os bits 15:12 são reservados, o bit 11 permite que esteja ou não habilitado o bit de paridade, o bit 10 e 9 não têm significado para a configuração de SSRAMS, o bit 8 representa se é ou não autorizada a escrita, os bits 7:6 indicam o tamanho da memória, os bits 5:4 indicam a largura do barramento de dados, os bits 3:1 indicam que tipo de memória está a ser configurada, e o bit 0 permite que o chip select desta memória esteja activo ou não. Assim para termos a SSRAM configurada, o valor deste registo será: Posição dos bits Access Funcionalidade Valor 31:24 ro reserved -- 23:16 rw Base address 8 h1000_0000 15:12 ro resrved -- 11 rw Parity enable 1 b0 10 rw Keep row open 1 b0 Sem interesse para SSRAMs 9 rw Bank address select 1 b0 Sem interesse para SSRAMs 8 rw Protecção de escrita 1 b0 7:6 rw Tamanho da memória 2 b00 Sem interesse para SSRAMs 5:4 rw Tamanho do barramento de dados 2 b10 Para SSRAMs este valor deve ser sempre de 32bits 3:1 rw Tipo de memória 3 b001 SSRAMs 0 rw Activa ou inibe o chip 1 b1 select Tabela 21: Configuração do Chip select register para uma memória SSRAM

Hardware/Software co-design para aplicações de processamento de voz 89 Ainda restam mais alguns registos que devem ser configurados. Estes são o tms, time select register que define os diferentes parâmetros temporais para as memórias que se encontram presentes no sistema. No entanto, de acordo com a documentação, este registo não tem significado quando se trata de SSRAMS pelo que o valor indeterminado para os seus bits serve perfeitamente. Ainda existe um outro registo, o control status register, que se destina a gerar os intervalos de refrescamento orientados para as SDRAMS. Para melhor conhecimento dos vários campos deste registo, é aconselhada a consulta do manual do controlador. 7.3.3 Aceder aos registos de configuração e ao espaço de memória Para que se possa escrever nos registos de configuração é necessário um descodificador de endereços. Basicamente é necessário que os endereços de entrada tenham um determinado valor para que se possa aceder a um certo registo de configuração. Por essa mesma razão coloca-se uma tabela retirada a partir do manual que mostra como se pode aceder a cada um destes registos especiais orientados para executar a configuração. Basicamente o que acontece é que uma macro é colocada com o valor lógico 1, quando há correspondência entre o endereço de entrada e aquele que dá acesso à escrita nestes mesmos registos.

Hardware/Software co-design para aplicações de processamento de voz 90 Figura 24:Registos de configuração presentes no controlador de memórias À semelhança do que acontece com o caso dos registos, também aqui deve ser feita a descodificação associada às memórias. O procedimento é equivalente. Uma macro fica activa quando há correspondência entre o endereço de entrada e aquele a que a memória deve responder. As configurações referidas são implementadas no ficheiro mc_defines.v, onde após a sua consulta se compreende com os comentários incorporados o procedimento para esta tarefa. Para compreender a forma como se realiza a descodificação consideremos um exemplo bastante simples. Consideremos que para configurar os registos estes deviam responder aos endereços 32 h6000_000xx. Para que a macro responsável pela selecção dos registos, MC_REG_SEL é essencial que a comparação dos bits 31 ao 29 do endereço de entrada, seja realizada com 001. Neste caso sempre que se pretende aceder a um registo para uma determinada configuração, esta macro fica activa. Do ponto de vista sintáctico será: `define MC_REG_SEL wb_addri[31:29] == 3 b001

Hardware/Software co-design para aplicações de processamento de voz 91 Assim, se quisermos aceder ao chip select 4 para a sua configuração, 32 h6000_0030, esta macro fica activa e então temos acesso ao registo pretendido. Da mesma forma que se executa esta operação para a a selecção dos registos, para as memórias a operação é idêntica. Imaginemos que queremos aceder aos endereços 32 h1000_0000, então a macro deve estar definida da seguinte forma, relembrando que devemos realizar a comparação entre os bits mais significativos do endereço de entrada, com o valor que pretendemos para a escrita/leitura da memória esteja activa. Deste modo a compração deve ser a seguinte: `define MC_MEM_SEL wb_addri[31:28] == 4 b0001 Neste caso qualquer endereço que satisfaça este requisito terá acesso à memória. As macros MC_REG_SEL e MC_MEM_SEL, permitem tratar da parte da descodificação que tem de ser obrigatoriamente implementada. A macro MC_HAVE_CS permite indicar o número de chip selects que se pretender ter no controlador. Atenção que a macro MC_HAVE_CS1 tem de estar sempre presente. Existe ainda uma outra macro de interesse, que é o caso da memória que é seleccionada para o arranque, MC_DEF_SEL. Além disso, para esta situação, existe uma macro que contém o valor do registo temporal, MC_DEF_POR_TMS. Para qualquer detalhe adicional, a consulta da documentação é a indicada. Com o controlo sobre estas macros presentes no ficheiro é possível configurar o sistema de acordo com os dispositivos que se encontram presentes. O exemplo do testbench permite ilustrar e compreender como realizar a configuração. 7.3.4 Testbench do controlador de memórias Obviamente como em qualquer módulo descrito em linguagem comportamental, é necessário que este seja validado com o recurso de um testbench. Assim neste caso o modelo que iremos testar será o seguinte:

Hardware/Software co-design para aplicações de processamento de voz 92 Modelo de um master que comunica em wishbone Wishbone Memory Controller Data Addr Contro Modelo da Ram Figura 25:Modelo usado no testbench 7.3.5 Descrição das unidades auxiliares para teste O modelo do master que existe no testbench é capaz de implementar o protocolo de comunicação wishbone e de um conjunto de rotinas que serve para a escrita, leitura e comparação de valores em determinadas posições de memória. De acordo com o modelo implementado para este as rotinas wbm.wb_read (stb_delay, cyc_delay, address, data), wbm.wb_write (stb_delay, cyc_delay, address, data) e wbm.wb_cmp (stb_delay, cyc_delay, address, data) permitem fazer a leitura, escrita, e comparação de um determinado valor no endereço indicado, sendo possível diagnosticar erros que estejam a acontecer, quer por time_out ou por erros de leitura ou de escrita. Neste modelo também se encontram presentes handlers destinados à contagem de erros quer seja até ao momento presente da análise e no final do simulação. Este modelo está presente na secção de anexos e pode ser consultado para ver as questões de detalhe relativamente ao protocolo Wishbone. Ainda existem alguns módulos adicionais que servem para testar o controlador de memórias. Podem referir-se o módulo watchdog, que verifica as questões de time-out, como o módulo que verifica se ocorreram erros, através da análise do sinal wishbone error, um módulo que verifica se ocorreu erro no chip select quer por estar indefinido quer pelo acesso de vários chip selects no mesmo instante temporal. São estes os módulos que temos de usar para testar a validade do controlador de memórias.

Hardware/Software co-design para aplicações de processamento de voz 93 No testbench default que foi obtido na pasta mem_ctrl/bench/richard/verilog/ está todo o código fonte que é usado no testbench e com esses ficheiros fonte, incluindo o mc_defines.v que se deve realizar a simulação. Resta então mostrar que tipo de SSRAM, que vamos testar. Esta é uma memória da Micron MT58L1MY18D, é a memória que serve de teste. Na realidade são duas memórias deste género que são usadas no controlador. A datasheet desta SyncBurst memory pode ser encontrada na página: www.knt.vein.hu/cnnal/tantargyak/digitalis_rendszerek/ssram_mt58l1my18d_16.pdf. Esta memória em termos gerais é do tipo SSRAM, que na versão que se esta a usar é 1Megx18, ou seja 1 Meg posições de memória de 18 bits e que suporta bursts tendo na sua arquitectura dois bits que permitem o incremento do endereço interno, o que está sempre associado a memórias deste género. Possui como entradas síncronas, incluem todos os endereços, dados, chip enable que funciona no nível lógico baixo, dois chips enable adicionais que permitem mais facilidade na expansão em profundidade, e sinais de controlo de bursts (ADSC#, ADSP#, ADV#), controlo dos bursts relacionados com cada um dos bancos de dados presentes através de BWa# e BWb# já que existem 2 bancos, e uma entrada que permite a escrita global GW#. A operação de burst pode ser iniciada com ADSP e ADSC. Os endereços subsequentes são controlados por ADV, burst advance input. Como se pode fazer a escrita/leitura usando bytes, então aos dados do bit 0 ao 7, e do 8 ao 15 no caso da escrita, estarão associados os sinais BWa e BWb. As entradas assíncronas são o output enable que permite disponibilizar os dados para o exterior, a entrada de snooze enable que coloca em suspensão a memória e a entrada mode que permite escolher entre bursts lineares ou interleaved. Para qualquer detalhe relativamente quer a entradas ou saídas que estejam aqui mencionadas deve ser feita a consulta da datasheet que se encontra no site indicado. No entanto irá apresentar-se aqui a forma como são gerados os bursts, e as formas de onda, para o caso da leitura e da escrita.

Hardware/Software co-design para aplicações de processamento de voz 94 Figura 26: Modo de geração dos endereços quando se realizam bursts Figura 27: Timings e formas de onda associados à leitura da memória da Micron

Hardware/Software co-design para aplicações de processamento de voz 95 Figura 28:Timings e formas de onda que estão associados à memória da Micron Convém salientar que existe um modelo descrito em verilog e que reproduz fielmente a memória cujas características foram apresentadas na secção anterior e que está na pasta mem_ctrl/bench/richard/verilog/models/. Se este ficheiro for analisado com atenção verificar-se-á que existem os bancos de memória, dois como foi referido e o incremento interno do endereço quando se realizam bursts lineares. Além disso é possível observar em que situação é que o sinal de output enable está activo, assim como aquela em que é permitida a escrita de um determinado banco de memória. As condições estão explícitas e respeitam a tabela de verdade que pode ser encontrada na datasheet desta mesma memória. Todos os sinais que estão definidos no modelo verilog respeitam a tabela de verdade referida.

Hardware/Software co-design para aplicações de processamento de voz 96 7.3.5.1 Testes pretendidos para o modelo desenvolvido e concretização O teste que os devellopers do controlador de memória implementaram, verifica o acesso sequencial dos endereços e numa fase posterior a acção de burst preenchendo a memória no sentido do endereço mais elevado para o menos elevado. A sequência de operações a realizar de acordo com o testbench correspondem à programação do controlador de memórias: wbm.wb_write(0, 0, 32'h6000_0008, `BA_MASK); // program base address register wbm.wb_write(0, 0, 32'h6000_0000, 32'h6000_0400); // program CSR Figura 29: Programação da mascara para o base address e do control status register Seguido posteriormente pela chamada a uma task onde se faz a configuração do Chip Select Configuration Register e do Timing Select Register. hardwire???) csc_data = { ; 8'h00, SSRAM_SEL, 4'h0, 1'b1, 1'b0, 1'b0, 1'b0, 2'b00, 2'b10, 3'b001, 1'b1 // tms_data is unused for ssrams tms_data = { 32'hx ; // reserved // SEL // reserved // parity enabled // KRO, no meaning for ssram // BAS, no meaning for ssram // WP // MS, no meaning for ssram // BW == 32bit bus. Always for ssram (maybe // MEM_TYPE == SDRAM // EN == chip select enabled // program chip select registers $display("\nprogramming SSRAM chip select register."); wbm.wb_write(0, 0, 32'h6000_0030, csc_data); // program cs4 config register (CSC4) $display("programming SSRAM timing register."); wbm.wb_write(0, 0, 32'h6000_0034, tms_data); // program cs4 timing register (TMS4) Figura 30:Configuração do CSC register e TMS register Repare-se que o CSC register está escrito na posição 30 a que corresponde ao CSC4, o que concorda com a tabela de endereços presente na secção que foi descrita. Seguidamente realizam-se os acessos à memória, efectuando as escritas e testando se os valores lidos estão ou não de acordo como esperado. São também testados se os sinais Wishbone estão de acordo com o protocolo descrito na especificação do mesmo.

Hardware/Software co-design para aplicações de processamento de voz 97 Se o ficheiro relativo ao teste sequencial de acesso à memória Anexo 7, leitura e escrita da mesma, for consultado e se confrontar o mesmo com a execução apresentada, estes estão de acordo. Repare-se que não foi logo na primeira tentativa que se conseguiu realizar a configuração do controlador, já que uma das macros necessárias à realização do mesmo não estava activa, MC_HAVE_CS4, que estava ligada directamente à memória da Micron. Esta foi uma das dores de cabeça. Apesar de ter ocorrido este problema, houve a oportunidade de ver alguns detalhes em relação ao wishbone e diagnosticar porque razão o controlador atingia o tempo máximo do watchdog, e caso este fosse desligado em que zona do código se encontrava. Foi essencial verificar que sinais deveriam modificar o seu estado e porque razão isto não era efectuado. O resultado da execução do testbench é o seguinte: MC_TIMING SM: Entered non existing state... ( 0.0 ns) # INFO: WISHBONE MASTER MODEL INSTANTIATED (bench_top.wbm) # --- SSRAM SEQUENTIAL ACCESS TEST --- # Programming SSRAM chip select register. # Programming SSRAM timing register. # SSRAM sequential test. BL = 1, CYC-delay = 0, STB-delay = 0 # Filling SSRAM memory... # Verifying SSRAM memory contents... # SSRAM sequential test. BL = 2, CYC-delay = 0, STB-delay = 0 # Filling SSRAM memory... # Verifying SSRAM memory contents...... # Current errors detected: 0 # Total errors detected: 0 Figura 31: Resultado da execução do testbench default relativo ao memory controller 7.3.5.2 Formas de onda obtidas e o protocolo wishbone

Hardware/Software co-design para aplicações de processamento de voz 98 Nada melhor que esta altura, para mostrar as formas de onda, associando-as com as especificações dos sinais de wishbone. Iremos observar então a escrita e a leitura de um determinado endereço, e verificar que tal acontece de acordo com as directivas indicadas neste protocolo. O documento que especifica este mesmo protocolo pode ser encontrado em http://www.opencores.org/projects.cgi/web/wishbone/wbspec_b3.pdf, sendo que a informação disponibilizada no capítulo actual deste documento foi retirada a partir do mesmo. Não é objectivo que neste documento se faça uma descrição exaustiva do protocolo a nível dos seus sinais, mas ilustrar o seu funcionamento no contexto do controlador. Para ter um conhecimento mais profundo acerca da funcionalidade de cada um dos sinais devem ser consultadas as páginas 32 à 36 do documento que foi referido. No entanto, apresentam-se os sinais que assumem maior relevância e os estados que devem possuir no caso de leitura ou de escrita. Sinal Funcionalidade The acknowledge input [ACK_I], when asserted, indicates the [ACK_I] normal termination of a bus cycle. ADR_O() The address output array [ADR_O()] is used to pass a binary address. CYC_O The cycle output [CYC_O], when asserted, indicates that a valid bus cycle is in progress. ERR_I The error input [ERR_I] indicates an abnormal cycle termination. LOCK_O RTY_I SEL_O() STB_O The lock output [LOCK_O] when asserted, indicates that the current bus cycle is uninterruptible. The retry input [RTY_I] indicates that the interface is not ready to accept or send data, and that the cycle should be retried. The select output array [SEL_O()] indicates where valid data is expected on the [DAT_I()] signal array during READ cycles, and where it is placed on the [DAT_O()] signal array during WRITE cycles. The strobe output [STB_O] indicates a valid data transfer cycle. Tabela 22: Lista de sinais associados ao protocolo wishbone e respectiva funcionalidade (obtidos a partir do documento de wishbone)

Hardware/Software co-design para aplicações de processamento de voz 99 Iremos apresentar seguidamente o resultado da simulação e os respectivos sinais de wishbone, acompanhados pela sequência de leitura/escrita. Figura 32: Formas de onda associadas à escrita/leitura usando o protocolo wishbone CLOCK EDGE 0: Leitura MASTER presents a valid address on [ADR_O()] MASTER negates [WE_O] to indicate a READ cycle. MASTER presents bank select [SEL_O()] to indicate where it expects data. MASTER asserts [CYC_O] to indicate the start of the cycle. MASTER asserts [STB_O] to indicate the start of the phase. SETUP, EDGE 1: SLAVE decodes inputs, and responding SLAVE asserts [ACK_I]. SLAVE presents valid data on [DAT_I()] SLAVE asserts [ACK_I] in response to [STB_O] to indicate valid data. MASTER monitors [ACK_I], and prepares to latch data on [DAT_I()] CLOCK EDGE 1: MASTER latches data on [DAT_I()]. MASTER negates [STB_O] and [CYC_O] to indicate the end of the cycle. SLAVE negates [ACK_I] in response to negated [STB_O]. ------------------------------------------Escrita---------------------------------------------- CLOCK EDGE 0: MASTER presents a valid address on [ADR_O()] MASTER presents valid data on [DAT_O()] MASTER asserts [WE_O] to indicate a WRITE cycle. MASTER presents bank select [SEL_O()] to indicate where it sends data. MASTER asserts [CYC_O] to indicate the start of the cycle. MASTER asserts [STB_O] to indicate the start of the phase.

Hardware/Software co-design para aplicações de processamento de voz 100 SETUP, EDGE 1: SLAVE decodes inputs, and responding SLAVE asserts [ACK_I]. SLAVE prepares to latch data on [DAT_O()]. SLAVE asserts [ACK_I] in response to [STB_O] to indicate latched data. MASTER monitors [ACK_I], and prepares to terminate the cycle. CLOCK EDGE 1: SLAVE latches data on [DAT_O()] MASTER negates [STB_O] and [CYC_O] to indicate the end of the cycle. SLAVE negates [ACK_I[ in response to negated [STB_O]. Figura 33:Descrição dos sinais de wishbone Como se pode observar, as formas de onda estão de acordo com a convenção indicada na figura anterior. Por último basta mostrar o funcionamento da memória e o seu funcionamento em modo burst, sendo que no modelo indicado de verilog existe a correspondência inequívoca entre as entradas e saídas do componente físico. Quando se realiza a instanciação do mesmo no testbench disponível no Anexo 8, para se poder simular a configuração será a seguinte (os comentários permitem compreender o modo como é realizada esta tarefa). mt58l1my18d ssram1 (.Dq( {par_con[8], par_con[0], mc_dq[15:0] ),.Addr(mc_adr_o[19:0]),.Mode(1'b0), // This input (sometimes called LBO) selects burst order // 1'b0 = linear burst, 1'b1 = interleaved burst.adv_n(mc_adv_o),.clk(mc_clk),.adsc_n(mc_adsc_o),.adsp_n(1'b1),.bwa_n(mc_dqm_o[1]),.bwb_n(mc_dqm_o[0]), // or the otherway around.bwe_n(mc_we_o),.gw_n(1'b1),.ce_n(mc_cs_o[4]),.ce2(1'b1),.ce2_n(1'b0),.oe_n(mc_oe_o),.zz(mc_zz_o) ); Figura 34: Memória instanciada no sistema É essencial observar os Sinais de chip enable, output enable, write enable, sinais de burst, dados e endereços relacionados com a ram.

Hardware/Software co-design para aplicações de processamento de voz 101 Figura 35: Resultado da simulação em relação aos sinais da SSRAM Aqui é evidenciado sem dúvida o incremento interno dos endereços a ser usados para escrita. Como estamos a usar o mode de burst linear a contagem está de acordo com o sinal bcount o que, consultando a tabela apresentada acerca deste assunto, está correcto. Repare-se que a descodificação está correcta. Como neste momento foram verificados as operações descritas para a memória da Micron, uma vez que esta é a que o testbench traz por default, é essencial que se faça o teste agora com a memória da Cypress. As datasheets estão de acordo entre si, quer seja a nível de IOs como funcionalidades implementadas. A nível do testbench as modificações que devem ser implementadas reflectem-se na desactivação das SSRAMs, e colocar esses sinais orientados para a memória da Cypress cujo modelo está no Anexo 11, foi já descrito numa secção anterior deste documento. Esta memória na versão implementada não aceita bit de paridade. A modificação será então a seguinte: CY7C1340G_PLDCD cypress (.ZZ(mc_zz_o),.Mode(1'b0),.ADDR(mc_adr_o[16:0]),.GW_N(1'b1),.BWE_N(mc_we_o),.BWd_N(mc_dqm_o[3]),.BWc_N(mc_dqm_o[2]),.BWb_N(mc_dqm_o[1]),.BWa_N(mc_dqm_o[0]),.CE1_N(mc_cs_o[4]),

Hardware/Software co-design para aplicações de processamento de voz 102.CE2(1'b1),.CE3_N(1'b0),.ADSP_N(1'b1),.ADSC_N(mc_adsc_o),.ADV_N(mc_adv_o),.OE_N(mc_oe_o),.DQ (mc_dq),.clk (mc_clk) ); Figura 36: Modificação para fazer uso da memória da Cypress Para correr o testbench deve ser desactivado no registo de chip select o bit de paridade uma vez que este não é suportado por este. Quando se realizam os testes com a memória da Cypress, os resultados são idênticos aos da memória da Micron, o que demonstra a validade da sua implementação. Tanto os displays que se obtém na consola, como as ondas obtidas para os sinais estão correctos. Figura 37: Forma de ondas obtidas com a memória da Cypress Agora que o controlador de memórias, está testado é conveniente fazer a sua integração com o Or1k, tendo como base alguns aspectos essenciais e que serão abordados no capítulo seguinte de forma mais detalhada. 7.4 Or1k e memory controller: necessidade de um arbitrador O sistema que apresentamos para ser integrada com a memória da cypress exige que haja um sistema capaz de arbitrar os acessos à memória.

Hardware/Software co-design para aplicações de processamento de voz 103 Mais uma vez fazendo uso da variedade de cores disponíveis no site dos Opencores, www.opencores.org, encontrou-se um que com base na linguagem Perl, produzia o código em vhdl que implementava um sistema master/slave, onde e esse aspecto tem de ser reforçado todas as interfaces eram implementadas com o protocolo wishbone. Este pode ser encontrado em: http://www.opencores.org/projects.cgi/web/wb_builder/overview estando o código disponível assim como a sua respectiva documentação. Para poder iniciar a implementação deste mesmo módulo existe um script em perl que permite realizar essa mesma operação. O script permite definir o número de masters, o número de slaves e o respectivo espaço de endereçamento para cada um destes últimos. Como no caso em análise é pretendido ligar dois masters, a cache de dados e de instruções a uma mesma memória, que será o slave, sem dúvida que esta aplicação serve os objectivos. Depois de se ter feito o download do ficheiro de perl este deve ser invocado da seguinte forma : perl wishbone.pl Assim temos acesso a uma série de menus que permite fazer a configuração pretendida e ter o ficheiro fonte que permite fazer a ligação entre as caches e a memória. Pode-se alternativamente fazer as configurações, tendo como base as definições presentes no ficheiro wishbone.defines, Anexo 10. As configurações estão coerentes com o manual que pode ser retirado do link referido. Iremos mostrar que configurações é que estão a ser feitas :

Hardware/Software co-design para aplicações de processamento de voz 104 Figura 38 Configuração do Wishbone builder Para obter o vhdl sem a configuraçãp anterior deve ser invocado o seguinte comando perl wishbone. pl noguide wishbone.defines Com base nas especificações pretendidas está-se apto a ter o ficheiro vhdl produzido. Repare-se que para os masters e slaves os sinais de wishbone principais foram os definidos na secção anterior. Os endereços para o slave responder, devem incluir a configuração da memória e dos registos como foi salientado. No exemplo indicado para os registos do controlador temos o endereço 32 h6000_0000 e para a memória o endereço 32 h1000_0000. Os espaços para cada um dos blocos a responder são os suficientes. Esse ficheiro permitirá a ligação entre dois modelos de master definidos já no testbench anterior e que têm a correspondência com as caches. É apresentado no Anexo 17 o esquema relativo à síntese deste master slave e que mostra claramente o interface wishbone para masters e slave. Deve ser referido que no vhdl produzido existe um erro pois há números fraccionários que necessitam de arredondamento para inteiros e que estão no módulo de descodificação. Este pormenor deve ser referido. Refira-se ainda que deve ser usado um valor elevado para o somatório das prioridades para evitar bloqueios na simulação.

Hardware/Software co-design para aplicações de processamento de voz 105 Figura 39: Modelo Master Model para teste Modelo do master 1 Wishbone Data Modelo do master 2 Wishbone Master Slave Wishbone Memory Controller Addr Contro Ram O ficheiro vhdl encontra-se presente na mesma directoria em que foi invocado o script, e está em anexo neste relatório. É também colocado o testbench que reúne o modelo de dois masters e faz a ligação entre estes e o master/slave o controlador de memória e a SSRAM da cypress. 7.4.1 Testbench para o sistema master/slave Para testar o modelo referido no esquema anterior, foi necessário instanciar dois master models e o master slave fazendo a sua ligação, estando o resto do sistema configurado como nos testbenchs anteriores. Isto está de acordo com o Anexo 13. O primeiro teste será o seguinte: a=0; while (1 && a < 2000) begin $display (" Escrita do master 1 \n"); wbm.wb_write(20, 10, 32'h6000_0008, `BA_MASK); // program base address register $display (" leitura e comparação do master 1\n"); wbm.wb_cmp(0, 0, 32'h6000_0008, `BA_MASK); $display (" Escrita do master 2 \n"); wbm2.wb_write(0, 0, 32'h6000_0000, 32'h6000_0400); // program CSR // check written data $display (" leitura e comparação do master 2\n"); wbm2.wb_cmp(0, 0, 32'h6000_0000, 32'h6000_0400); a=a+1; end

Hardware/Software co-design para aplicações de processamento de voz 106 As operações correspondem a uma escrita e leitura do master 1 seguidas de uma escrita e leitura do master 2. Os resultados na consola mostram a alternância entre estes e os respectivos acessos. Figura 40 : Teste do master model com acessos alternados A análise das formas de onda revela que os acessos são alternados e que os sinais do módulo master/slave responsável pelo arbitragem do uso do barramento de dados, estão sempre simétricos em relação aos masters,, o que mostra que os acessos estão a ser realizados de forma coerente. Um outro teste que se realizou foi o master 2 estar permanentemente a tentar aceder ao barramento para escrever nos registos csr e ba_mask e o outro a executar as operações

Hardware/Software co-design para aplicações de processamento de voz 107 de teste da memória que se realizaram nos testbenchs anteriores. Este ficheiro é o Anexo 14. Figura 41: Resultados da execução do testbench com acesso concorrente dos masters O último dos testes feitos, constituiu em ler um conjunto de valores carregados na memória, que terá utilidade importantíssima no desenvolvimento posterior deste mesmo trabalho. 8 Adaptação da norma do codec AMR à arquitectura do processador OpenRISC 1200 Nesta fase do trabalho procedeu-se à adaptação do código C que é fornecido pela norma do codec à arquitectura do processador OpenRISC 1200. A principal alteração realizada aqui foi a adaptação das operações básicas definidas no ficheiro basic_op.h e basicop2.c de forma a estas serem implementadas o mais eficientemente possível. 8.1 Definição dos tipos de variáveis no OpenRISC1200

Hardware/Software co-design para aplicações de processamento de voz 108 Ao longo do código necessitamos de variáveis de 8 bits, 16 bits e 32 bits, que devem ser definidas como Word8, Word16 e Word32 para além de uma variável de 32 bits definida como flag. Estas variáveis são definidas no ficheiro typedef.h que, em função da plataforma onde é compilado o programa, define as variáveis de acordo com o seu número de bits. É claro que este ficheiro não consegue reconhecer a plataforma de compilação do OR1k. Sendo assim, definiu-se directamente o tipo de variáveis com o tipo de dados que é mais habitual para 32, 16 e 8 bits e esperou-se que se tenha acertado. O ficheiro typedef.h foi alterado como se mostra a seguir. Testes posteriores e recorrendo-se ao código assembly verificou-se que a definição do tipo de variáveis está correcta. /* ******************************************************************************** * * GSM AMR-NB speech codec R98 Version 7.6.0 December 12, 2001 * R99 Version 3.3.0 * REL-4 Version 4.1.0 * ******************************************************************************** * * File : typedef.c * Purpose : Basic types -> OR1k * ******************************************************************************** */ #ifndef typedef_h #define typedef_h "$Id $" typedef signed char Word8; typedef short Word16; typedef int Word32; typedef int Flag; #endif 8.2 Funções não reconhecidas pelo compilador do OpenRISC1200 Existe ao longo do código C chamadas a funções que não são aceites pelo compilador do processador. Estas funções estão associadas a chamadas ao sistema operativo ou então relacionam-se com a saída ou entrada de dados. Estas funções são as seguintes: malloc; free; fprintf; printf; abort;

Hardware/Software co-design para aplicações de processamento de voz 109 fwrite; fread. Começou-se por alterar as duas primeiras. Estas (malloc e free) são responsáveis pela alocação e libertação dinâmica de memória para as estruturas definidas ao longo de todo o código. Estas funções realizam chamadas ao sistema operativo. Existem duas soluções possíveis para se contornar este problema. A primeira seria a implementação dum micro sistema operativo no processador. Desta forma seria possível alocar dinamicamente memória recorrendo a estas duas funções. Esta solução não era a mais rápida e eficiente dado que o codec não necessita de um sistema operativo de suporte e iria ocupar mais recursos na FPGA. A outra solução é simplesmente abandonar-se a estrutura de alocação dinâmica de memória e declarar-se estas variáveis normalmente como sendo globais. A declaração dinâmica de variáveis está sempre associada às estruturas existentes no código (designados módulos), que recorrem às funções func_init, func_reset e func_exit já explicadas em secções anteriores. A função func_init é a responsável pela alocação da memória. Esta função deve então ser alterada para todos os módulos. De seguida apresenta-se um exemplo de uma função que inicializa um destes módulos. /* ************************************************************************** * * Function : agc_init * Purpose : Allocates memory for agc state and initializes * state memory * ************************************************************************** */ int agc_init (agcstate **state) { agcstate* s; if (state == (agcstate **) NULL){ fprintf(stderr, "agc_init: invalid parameter\n"); return -1; *state = NULL; /* allocate memory */ if ((s= (agcstate *) malloc(sizeof(agcstate))) == NULL){ fprintf(stderr, "agc_init: can not malloc state structure\n"); return -1; agc_reset(s); *state = s; return 0;

Hardware/Software co-design para aplicações de processamento de voz 110 Este módulo foi então modificado de forma a se ter o seguinte código. /* ************************************************************************** * * Function : agc_init * Purpose : Initializes state memory for agc state * ************************************************************************** */ int agc_init (agcstate **state) { if (state == (agcstate **) NULL){ fprintf(stderr, "agc_init: invalid parameter\n"); return -1; *state = NULL; agc_reset(&agcstate_s); *state = &agcstate_s; return 0; Note-se que agora a função de inicialização só tem que fazer o reset às suas variáveis chamando a função func_reset. Este processo foi realizado para todos os módulos existentes no código. A questão que se teve de ter em atenção quando se fez esta operação é que não podemos chamar duas vezes a mesma função de inicialização, sob pena de não alocarmos uma nova variável mas sim alterar a já existente. Este facto foi importante na inicialização da estrutura gc_predstate. Esta estrutura é declarada duas vezes pela estrutura gainquantsatate e uma pela estrutura Decoder_amrState. O que se fez aqui foi criar três funções distintas de inicialização de três variáveis distintas mas todas elas do tipo gc_predstate. Quanto à função responsável pelo reset, manteve-se inalterada. Uma vez que deixamos de ter alocação dinâmica de memória já não precisamos de recorrer à função de exit. Esta simplesmente foi eliminada do código. A função de fprintf deixa de fazer sentido na implementação no OR1k, pelo que foi eliminada. Para se evitar o desagradável trabalho de eliminar à mão todas as chamadas a esta função definiu-se a seguinte macro: #define fprintf{x,y { Esta macro substitui todas as chamadas à função fprintf que recebam dois parâmetros (que são as únicas existentes no código) por uma função vazia e foi colocada no ficheiro typedef.h por ser aquele que é incluído por todos os outros.

Hardware/Software co-design para aplicações de processamento de voz 111 As funções de printf e abort também deixam de fazer sentido e apenas surgem no ficheiro basicop2.c pelo que foram removidas manualmente. Quanto a fread e fwrite aparecem para leitura e escrita dos ficheiros que contêm as amostras de áudio a processar (ou processadas). Como pretendemos simular o codec no simulador do OR1k, onde não dispomos destas funcionalidades, estas funções serão removidas quando se preparar o código, à semelhança com o que se fez no teste da lei A, para simular uma amostra de sinal definida num vector no código C. Também se verificou que o compilador não reconhece a constante NULL. Recorreu-se então a uma macro colocada no ficheiro typedef.h para definir a constante: #define NULL 0 8.3 Optimização das operações básicas no OpenRISC1200 Como já foi dito, a norma do codec recorre a uma série de operações básicas para realizar todas as operações aritméticas sobre as variáveis ao longo da codificação/descodificação dum sinal. Estas operações encontram-se definidas como funções nos ficheiros basic_op.h e basicop2.c. 8.3.1 Escrita de instruções assembly em código C Para se poder fazer uma eficiente optimização ao código C é necessário recorrer-se à escrita de partes do código em linguagem assembly. O objectivo aqui é mesmo reescrever todas as operações básicas definidas pela norma do codec AMR para assembly. A escrita de uma instrução em assembly é feita recorrendo-se à directiva asm, como se mostra a seguir: asm("l.mul\tr5,r3,r9"); Neste pequeno exemplo pode-se ver uma invocação à instrução em assembly de multiplicação l.mul que multiplica o registo r3 por r9 e guarda o resultado em r5, isto por definição da instrução assembly usada. De uma forma geral não estamos interessados em usar directamente registos mas sim variáveis declaradas no código C e associá-las então aos registos das instruções em assembly. Isto pode ser feito como ilustra o seguinte exemplo:

Hardware/Software co-design para aplicações de processamento de voz 112 asm("l.mul\t%0,%1,%2" : "=r" (out) : "r" (var1), "r" (var2) ); Esta instrução tem 3 parâmetros (registos no caso). Estes são invocados recorrendose à sintaxe de %0, %1 e %2. Os números indicam a ordem com que as variáveis do lado direito da expressão são invocadas. 0 significa a primeira variável, 1 a segunda variável e assim sucessivamente. Neste exemplo a primeira variável é out, a segunda var1 e a terceira var2. Estas variáveis, como se disse, devem ser associadas a registos. Isto é conseguido fazendo-se, por exemplo, "r" (var1), onde r significa um registo. Outro pormenor importante é a definição das variáveis/registos de saída. Estas devem ser chamadas da seguinte forma: : "=r" (out) :, sempre entre o carácter de dois pontos e com "=r". Note-se que se esta função fosse chamada da seguinte forma asm("l.mul\t%0,%1,%2" : : "r" (out), "r" (var1), "r" (var2) ); não ocorreria nenhum erro na compilação do programa. No entanto, quando se fosse correr o programa muito provavelmente ocorreriam erros, pois a variável de saída out tinha sido associada a um registo que não era preservado na continuação da execução do programa, sendo destruído o resultado da multiplicação. No seguinte exemplo pode-se ver o uso a uma instrução que recorre a valores imediatos (constantes). Isto é feito usando-se a letra i. asm("l.addi\t%0,r0,%1" : "=r" (out) : "i" (var1) ); Aqui, var1 terá que ser uma constante e não uma variável. Se não o for, o compilador (assemblador no caso) dá erro. 8.3.2 Colocação das operações básicas inline O primeiro teste que se fez foi a verificação se as operações básicas definidas pela norma eram ou não colocadas inline. É fundamental que estas funções não sejam chamadas recorrendo-se a saltos mas sim que sejam colocadas inline ao longo de todo o código. Só assim é possível ter-se um código que possa ser executado o mais eficientemente possível, pois o salto a uma função implica sempre o uso de uma instrução em assembly de salto e um número que pode ser

Hardware/Software co-design para aplicações de processamento de voz 113 elevado de instruções usadas para se preservar os valores dos registos, que vão ser usados na operação, na stack. É crucial que todas as operações estejam inline. Uma primeira vista ao código gerado pelo compilador, sem fazer qualquer alteração às operações básicas, revelou que estas funções não são colocadas inline. O que se fez foi então declarar as funções básicas como inline e observar as diferenças. Estas foram nulas. Quando se declara uma função como inline apenas aconselhamos o compilador a colocá-la inline, mas não o obrigamos. Numa tentativa de forçar o compilador a colocar estas funções inline usou-se as seguintes directivas de compilação: -finline-functions; -finline-limit=n; com n = {600, 800, 1000, etc.; -O3 Os resultados foram mais uma vez nulos. Chegou-se a fazer testes a uma chamada a uma função feita por nós que apenas usa uma instrução assembly. Mesmo assim o compilador não garantiu a sua colocação inline. Tal situação é impensável se queremos ter um código optimizado para correr em tempo em real. A solução encontrada foi abandonar esta técnica, que não garante uma colocação inline das funções, e recorrer-se ao uso de macros como é descrito na secção seguinte. 8.3.3 O uso de macros mas operações básicas O uso de macros foi necessário para se garantir sempre que as operações básicas existentes ao longo do código são colocadas inline. O conteúdo destas é substituído durante o pré-processamento sempre que existe uma chamada à respectiva macro. Desta forma as operações ficam sempre inline. O grande problema das macros é que estas, não sendo funções, não retornam valores. Para se ultrapassar este problema teve-se que passar à macro mais um parâmetro para além dos já existentes que será então o valor de retorno da operação.

Hardware/Software co-design para aplicações de processamento de voz 114 Desta forma temos que alterar manualmente todas as chamadas às operações básicas existentes no código. Esta solução torna-se muito dispendiosa em termos de tempo, mas foi a única encontrada que realmente funciona. De notar que esta solução de alterar todo o código, permite níveis de optimização e adaptação à arquitectura do processador muito grandes. Por exemplo existem diferenças em multiplicar uma variável por outra variável ou multiplicar uma variável por uma constante. Se pretendermos escrever uma macro que realize uma multiplicação de forma genérica devemos recorrer à instrução em assembly que faz a multiplicação entre dois registos. Quando se chama esta macro para multiplicar uma variável por uma constante, o que o compilador irá fazer, em termos de assembly, é passar a constante para um registo auxiliar e depois usar a instrução de multiplicação, que está a ser forçada pela macro, de multiplicação entre dois registos. É muito mais eficiente criar duas macros distintas que realizem as multiplicações entre dois registos e entre um registo e um valor imediato recorrendo-se às respectivas instruções em assembly. Estes aspectos foram explorados quando se começou a fazer as substituições no código. Para se definir uma macro, a título de exemplo, fez-se o seguinte: #define mult_or32(var_out, var1, var2){ \ Word32 aux1; \ asm volatile ( "l.mul\t%0,%1,%2 "l.movhi\t%3,0x4000 "l.sfeq\t%3,%0 "l.bnf\t3 "l.srai\t%0,%0,0x000f "l.addi\t%0,r0,0x7fff : "=r" (var_out) \ : "r" (var1), "r" (var2), "r" (aux1) );\ Esta macro define a multiplicação segundo a norma do codec entre duas variáveis de entrada de 16 bits vindo a saída em 16 bits. De notar que o primeiro parâmetro é o resultado da operação. A invocação duma função em assembly fez-se acompanhar da palavra volatile para se garantir que o compilador nunca mexe no código assembly escrito por nós. No código em C devemos chamar esta função da seguinte forma: mult_or32 (a, x, y);

Hardware/Software co-design para aplicações de processamento de voz 115 para substituir o seguinte código: a = mult (x, y); 8.3.4 Alteração do código nas funções básicas De seguida apresenta-se uma breve descrição das operações básicas que foram implementadas em assembly para adaptar à arquitectura do OPENRISC1200. Para esta implementação, recorreu-se exaustivamente ao ficheiro basicop2.c onde estão definidas, tanto em C como textualmente, todas as operações básicas e às operações existentes em assembly de vírgula fixa do processador descritas no Anexo 5. A implementação realizada destas operações encontra-se no Anexo 6. A principal dificuldade desta parte do trabalho é o facto de quase todas estas operações, que são definidas pela norma do codec, exigirem que o resultado seja sempre saturado em caso de overflow. A arquitectura das operações do codec não contempla tais instruções. A norma também define que o resultado de todas as multiplicações deve vir multiplicado por 2. Como se vai proceder à alteração de todas as funções do codec, aqui não foi optimizado apenas o modo de 12.2 kbit/s mas sim todo o codec. Não vale a pena estarmos a seleccionar as funções associadas ao modo de mais alto débito do codec e fazer-se as alterações apenas aí. Para se validar as alterações feitas ao código usou-se uma técnica semelhante à descrita quando se validou o código da lei A. Usou-se as sequências de testes que podem ser encontradas junto com os ficheiros da norma do código C: spch_dos.inp; spch_dos.cod, spch_dos.out e allmodes.txt. Este último ficheiro altera em cada frame o modo de funcionamento do codec, pelo que é garantido que todos os modos são executados durante a simulação. 8.3.4.1 As flags de Overflow e Carry Estas duas variáveis são declaradas no ficheiro basic_op.h e são usadas nas operações básicas definidas pela norma e depois transportadas para o algoritmo do codec como variáveis globais para auxílio do seu processamento. Estas variáveis (flags) estão relacionadas com a saturação ou transporte do carry entre funções variáveis.

Hardware/Software co-design para aplicações de processamento de voz 116 Quando se proceder à optimização das funções em assembly é preciso ter-se em consideração o accionamento destas flags. Fazendo uma pesquisa a todos os ficheiros do algoritmo da norma não foi encontrada nenhuma invocação à flag de Carry com a excepção do ficheiro basicop2.c. Aqui, as operações que a usam também não são chamadas em parte alguma do codec. Podemos então concluir que a flag de Carry não é usada no algoritmo do codec pelo que podemos simplesmente ignorá-la. Quanto à flag de Overflow, é usada nos seguintes ficheiros: agc.c dec_amr.c g_pitch.c Como se tem um número tão reduzido de invocações a esta variável, vamos tentar eliminálas. Quando se procedeu à análise do estilo de programação usada pela norma, verificou-se que quando se pretendia verificar se um dado valor tinha saturado nas operações anteriores, apenas se comparava este mesmo com o valor máximo (ou mínimo) que este pode ter. Se fossem iguais, então é porque tinha ocorrido overflow. Por esta razão é porque aparece tão poucas vezes o uso à flag de Overflow. No ficheiro de g_picth.c pôde-se eliminar o uso da flag recorrendo-se a esta técnica. Em agc.c, a flag aparentemente não faz nada, pelo que simplesmente foi retirada do código. No ficheiro dec_amr.c, a invocação à flag aparece antes duma chamada a uma função. O que se fez aqui foi criar uma cópia dessa mesma função e tratar a situação de overflow da forma descrita acima. Uma alteração a esta função ficou fora de questão pois esta é chamada por outras partes do algoritmo que depois teriam de ser adaptadas. Neste ponto, não são necessárias as flags de Overflow e de Carry, pelo que a tarefa fica simplificada na optimização das operações básicas. 8.3.4.2 Instruções de adição/subtracção Para implementar estas instruções em que o resultado surge saturado em caso de overflow, usou-se a ideia simples de que uma soma entre dois operandos de sinais diferentes nunca origina overflow. Se os operandos tiverem o mesmo sinal e o resultado vier com o sinal contrário, então ocorreu overflow pelo que a saída deve ser saturada. Na subtracção fez-se algo de semelhante.

Hardware/Software co-design para aplicações de processamento de voz 117 Estas foram as instruções implementadas em assembly recorrendo-se a macros: add_or32 add_i_or32 L_add_OR32 L_add_i_OR32 sub_or32 L_sub_OR32 As instruções admitem operandos de 16 bits ou 32 bits (identificadas com L_). Sempre que possível recorreu-se a operações cujo segundo parâmetro de entrada é um valor imediato. Isto foi feito para se aproveitar as respectivas instruções em assembly disponibilizadas pelo processador. Aqui, é preciso ter em especial atenção ao uso da função L_add_i_OR32 que apenas permite valores imediatos de 16 bits no máximo. Quanto às substituições realizadas ao código teve-se em especial atenção o uso que a norma faz das operações de comparação. Aqui, todas as comparações são realizadas com o valor zero. Para tal, a norma recorre às operações de subtracção, comparando o resultado sempre com zero. No caso, isto não interessa, podendo simplesmente comparar duas variáveis uma com a outra. Ou seja, todas as operações existentes no código de subtracção dentro de if ou while foram eliminadas. 8.3.4.3 Instruções de multiplicação As multiplicações, de acordo com o que está definido pela norma, vêm sempre com o resultado multiplicado por 2. A saída deverá ser saturada em caso de overflow. Aqui, para se optimizar o código em assembly, teve-se em nota que a única possibilidade de ocorrer overflow, dado que os parâmetros de entrada destas funções são sempre em 16 bits, é quando estes são iguais ao valor mais negativo (-32768). As operações implementadas foram as seguintes: mult_or32 mult_i_or32 L_mult_OR32 L_mult_i_OR32 Mais uma vez criou-se operações que admitem como entrada valores imediatos. Os valores de entrada são sempre em 16 bits e os de saída em 16 ou 32 conforme o caso.

Hardware/Software co-design para aplicações de processamento de voz 118 8.3.4.4 Instruções de normalização As operações implementadas foram as seguintes: norm_s_or32 norm_l_or32 Estas duas funções retornam o número de shift lefts que o operando de entrada pode realizar sem que ocorra overflow. Por isso aparecem sempre associadas às funções de shift left. 8.3.4.5 Instruções shift left/right Nestas operações a dificuldade encontrada foi a implementação da função que realiza o shift left, pois este em caso de overflow deverá ser saturado. A técnica usada aqui foi recorrer-se a um ciclo que realiza um shift left de apenas 1 bit em cada iteração e, antes de se realizar a próxima iteração, verificar se o operando não irá entrar em overflow. Caso isto aconteça o ciclo deve ser parado e a função deve retornar um valor saturado. A implementação da instrução de shift right não apresentou qualquer dificuldade. As operações implementadas foram as seguintes: shr_or32 shr_i_or32 L_shr_OR32 L_shr_i_OR32 shl_or32 shl_i_or32 shl_n_sat_or32 shl_i_n_sat_or32 L_shl_OR32 L_shl_i_OR32 L_shl_round_OR32 L_shl_extract_h_OR32 L_shl_n_sat_OR32 L_shl_n_sat_OR32 L_shl_n_sat_extract_h_OR32 L_shl_n_sat_round_OR32

Hardware/Software co-design para aplicações de processamento de voz 119 Quanto às operações de shift right fez-se versões para parâmetros imediatos e para variáveis nas suas duas vertentes de 16 e 32 bits. As operações de shift left, dada a sua complexidade, tentou-se juntar com outras operações que habitualmente aparecem juntas a ela. Tipicamente um shift left aparece precedido de funções de normalização e procedido das funções de arredondamento ou de extracção da parte mais significativa da variável. Quando uma normalização aparece antes dum shift left, neste não é necessário fazer-se com saturação. 8.3.4.6 Instruções mac As instruções mac são, sem dúvida alguma, as mais críticas para realizar uma eficiente optimização de todo o codec. Por isso é preciso ter-se especial cuidado na sua implementação. Como, por definição da norma, o resultado da multiplicação vem sempre multiplicado por 2, adoptou-se aqui a estratégia de apenas fazer esta multiplicação no final de termos todos os valores acumulados, ou seja, quando se faz a leitura do acumulador. Desta forma, podemos usar directamente as instruções fornecidas pelo processador OR1k sem ter que estar sempre a realizar multiplicações por 2. Como temos um acumulador de 64 bits, assumimos que este nunca entra em saturação. Estas foram as instruções criadas: L_mac_init_0_OR32 L_mac_OR32 L_mac_i_OR32 L_msu_OR32 L_mac_out_OR32 L_mac_out_pos_OR32 L_mac_out_add_OR32 L_mac_out_add_pos_OR32 L_mac_out_round_OR32 L_mac_out_round_pos_OR32 Estas estão divididas em três grandes grupos: as funções de inicialização do acumulador da mac, as de mac propriamente ditas e as de leitura do acumulador da mac. A função de inicialização é responsáveis pelo esvaziamento do acumulador da mac.

Hardware/Software co-design para aplicações de processamento de voz 120 Criou-se três operações de mac para incrementar o seu acumulador. Estas três funções correspondem às três instruções que dispomos do processador. Uma para multiplicar e acumular duas variáveis outra para multiplicar e acumular uma variável por um valor imediato e outra para multiplicar e subtrair duas variáveis. A leitura do acumulador da mac é normalmente associada à operação de arredondamento. Estas duas operações foram então associadas. Como temos que realizar a operação de saturação do acumulador para 32 bits criouse instruções que apenas saturam a valores positivos. Isto é feito pois existe inúmeras vezes ao longo do código onde é sabido que o acumulador contém um valor positivo (cálculo de funções de autocorrelação por exemplo) e é mais fácil saturar só a valores positivos do que a valores positivos e negativos. Um pormenor muito importante que é preciso ter em atenção é ao facto de por vezes o acumulador da mac ter que ser inicializado com um dado valor. Como quando fazemos a leitura do acumulador multiplicamos este valor por 2, vamos também multiplicar esse valor inicializado por um factor de 2, o que originará valores incorrectos. Uma solução é inicializar a mac com metade do valor com que era anteriormente inicializada. Mas esta solução nem sempre é viável pois muitas das vezes não podemos simplesmente dividir esse valor por 2. Um exemplo disto é o inicializar a mac com o valor de 1. Se este valor for dividido por 2 o resultado (em inteiro) é 0. Para ultrapassar este problema optou-se por inicializar o acumulador da mac quando se faz a leitura do mesmo no final de um ciclo de incrementos. Em primeiro lugar multiplica-se o resultado final por 2 e só depois se realiza a soma que deveria ter sido feita no início. Isto resolveu o problema. Muitas das vezes é necessário ler-se o valor que se encontra no acumulador continuando este posteriormente a acumular dados. Por esta razão adoptou-se a estratégia das funções que lêem do acumulador nunca o colocarem a zero. Esta tarefa está incumbida à instrução de inicialização. Estas situações todas originam as funções apresentadas anteriormente.

Hardware/Software co-design para aplicações de processamento de voz 121 8.3.4.7 Outras instruções Foram também implementadas as seguintes operações básicas: abs_or32 L_abs_OR32 negate_or32 L_negate_OR32 div_s_or32 Estas realizam operações de cálculo do valor absoluto e de negar um valor tanto na sua vertente de 16 bits como de 32 bits. A operação de div_s realiza uma divisão especial onde numerador e denominador são valores positivos e o numerador deverá ser sempre menor ou igual ao denominador. O resultado desta operação é o resto desta divisão normalizado em 16 bits, ou seja, se numerador e denominador forem iguais o resultado será de 32767. 8.3.5 Problemas encontrados Quando se procedeu à substituição das funções básicas foi-se deparando com alguns problemas. O primeiro problema que surgiu logo à partida, foi o facto de as macros por vezes não darem resultados correctos quando os parâmetros passados a estas eram vectores, valores apontados ou estruturas. Este problema é aceitável e de certa forma compreendido, pois está-se a forçar que um parâmetro de entrada da macro seja algo de complexo. Quando se usa macros, estamos, neste caso, a forçar instruções em assembly no meio do código C. O compilador terá que fazer a ponte entre as variáveis usadas no código C e os registos que nós associamos às instruções em assembly. Quando se usam variáveis como vectores ou apontadores esta tarefa por vezes não é realizada correctamente pelo compilador. Para se ultrapassar este problema, declararam-se variáveis auxiliares onde se depositam as variáveis como vectores, valores apontados ou estruturas, sendo depois as primeiras usadas nas macros. Esta técnica resolveu o problema.

Hardware/Software co-design para aplicações de processamento de voz 122 Outro problema que surgiu foi o facto de algumas macros que necessitam de recorrer a variáveis auxiliares não funcionarem correctamente. Para explicar vejamos o seguinte exemplo: #define L_add_OR32(var_out, var1, var2){\ Word32 aux1;\ Word32 aux2;\ asm volatile ( "l.xor\t%3,%1,%2 "l.sfltsi\t%3,0x0 "l.bf\t10 "l.add\t%0,%1,%2 "l.xor\t%3,%1,%0 "l.sfgesi\t%3,0x0 "l.bf\t6 "l.sfgesi\t%1,0x0 "l.movhi\t%3,0x7fff "l.ori\t%3,%3,0xffff "l.movhi\t%4,0x8000 "l.cmov\t%0,%3,%4 : "=r" (var_out) \ : "r" (var1), "r" (var2), "r" ( aux1), "r" ( aux2));\ Repare-se como a última instrução assembly é codificada pelo compilador: L_add_OR32 (t0, L_temp1, L_temp2); f00171f0: e3 4a 60 05 l.xor r26,r10,r12 f00171f4: bd 9a 00 00 l.sfltsi r26,0x0 f00171f8: 10 00 00 0a l.bf f0017220 <_Lsp_Az+0x160> f00171fc: e0 6a 60 00 l.add r3,r10,r12 f0017200: e3 4a 18 05 l.xor r26,r10,r3 f0017204: bd 7a 00 00 l.sfgesi r26,0x0 f0017208: 10 00 00 06 l.bf f0017220 <_Lsp_Az+0x160> f001720c: bd 6a 00 00 l.sfgesi r10,0x0 f0017210: 1b 40 7f ff l.movhi r26,0x7fff f0017214: ab 5a ff ff l.ori r26,r26,0xffff f0017218: 1b 40 80 00 l.movhi r26,0x8000 f001721c: e0 7a d0 0e l.cmov r3,r26,r26 Como se pode ver as variáveis aux1 e aux2 foram confundidas uma com a outra. O compilador percebeu que estas variáveis eram a mesma e como tal deviam ser atribuídas ao mesmo registo, r26 no caso. Isto, como é óbvio, dá erro. Este problema pode ser contornado se as duas variáveis forem inicializadas com um valor qualquer (mas diferente um do outro). Problemas como este nem sempre surgem, ou seja, não se sabe à partida se o uso de variáveis que não sejam inicializadas dentro das macros podem ser confundidas uma com a outra pelo compilador. Algumas vezes não há problema, mas doutras dá erro. É necessário recorrer-se ao assembly gerado pelo compilador para verificar se houve erro. A desvantagem de se inicializar estas variáveis com um dado valor que não irá ser utilizado é que vamos gastar instruções para tais inicializações.

Hardware/Software co-design para aplicações de processamento de voz 123 Outros problemas surgiram devido não ao compilador mas sim ao simulador. O primeiro foi o uso da instrução de inicialização do acumulador da mac. Para tal recorreu-se à instrução de assembly l.macrc que lê para um registo o valor do acumulador e coloca-o a 0. Como só queremos limpar o acumulador da mac, associamos esta instrução ao registo r0 que está sempre com o valor 0 (hardwired) como se ilustra no exemplo seguinte: asm volatile ( "l.macrc\tr0\n" ); Alguns testes realizados sobre esta função revelaram não haver problemas. No entanto à medida que se continuaram a fazer os testes começaram a aparecer problemas. Aparentemente o programa a partir de uma de uma dada altura perde-se completamente, desalinhando mesmo a leitura das suas instruções. Se experimentarmos colocar duas vezes consecutivas esta mesma instrução (e tem que ser duas vezes!) quando se faz a leitura do valor de saída do acumulador na respectiva macro, o problema desaparece. Isto é um pouco estranho! Se em vez de usarmos o registo r0, usarmos outro qualquer registo associado à instrução l.macrc, já nada disto acontece. Verificou-se também que o simulador não reconhece a instrução assembly l.ff1. O compilador aqui não apresentou problemas, reconhecendo esta instrução. Ao longo do código foram surgindo situações onde simplesmente não se conseguiu fazer as substituições por macros. Estes casos aconteciam muito raramente e tentando analisar o código assembly não era visível qualquer erro. Numa tentativa de resolver este problema, e julgando-se que estes erros eram devido a uma má implementação do algoritmo das macros, chamou-se em simultâneo a macro e a função, comparando os resultados das duas. O valor retornado pela função era simplesmente ignorado. Surpreendentemente quando se fazia isto os erros deixavam de acontecer. Isto acontece porque o programa é alterado localmente. O compilador codifica então o código de maneira diferente, deixando de acontecer o erro. Tentou-se ainda mais duas soluções para tentar perceber o problema. A primeira foi a declaração das variáveis auxiliares na macro como volatile. Isto em nada alterou. A outra foi a passagem como parâmetros da macro destas variáveis auxiliares. Mais uma vez não houve efeitos.

Hardware/Software co-design para aplicações de processamento de voz 124 Este erro simplesmente não se consegue perceber. 8.4 Problemas do compilador No seguimento do que foi apresentado na secção anterior, pode-se especular sobre o possível mau funcionamento do compilador (ou simulador). De facto, há uma série de situações que não se compreendem muito bem porque é que acontecem e nalgumas delas o erro ainda não se conseguiu detectar (se é que é detectável). Existem duas situações bem distintas que ocorrem na compilação dum programa que podem indiciar que o compilador tenha sido adaptado de outras arquitecturas de outros processadores. A primeira é a situação onde existe um cast de uma variável de 32 bits para uma de 16 bits. O compilador comporta-se da seguinte forma: l.slli r18,r7,0x10 l.srai r18,r18,0x10 Aqui é feita uma cópia do registo r7 para o registo r18, sendo pelo meio feito um cast de 32 bits para 16 bits. A questão é que esta operação poderia ser feita recorrendo-se só a uma instrução assembly: l.exths r18, r7 Porque é que o compilador não faz isto? A outra situação é quando se pretende multiplicar uma variável por 5, por exemplo. O código gerado pelo compilador aparece assim: track = index * 5; f00165e4: bb aa 00 02 l.slli r29,r10,0x2 f00165e8: e3 7d 50 00 l.add r27,r29,r10 Repare-se que são usadas duas instruções assembly quando se poderia ter usado só uma: l.muli r27,r10,0x5 Esta última é mais eficiente, sob o ponto de vista de tempo de execução, do que a solução adoptada pelo compilador.

Hardware/Software co-design para aplicações de processamento de voz 125 Estas duas situações demonstram que não houve certos cuidados quando se criou o compilador. Mais ainda, pode indicar adaptações de outros compiladores de outros processadores que não foram totalmente bem sucedidas para o compilador do OR1k. No paper Using Open Source Cores in Real Applications, que pode ser encontrado em http://www.escet.urjc.es/~jcastillo/paperdcis.pdf, só vem afirmar os resultados obtidos. De acordo com o conteúdo desse documento, o que se afirma é que as ferramentas usadas para este processador, não são estáveis nem muito bem testadas, e quando usadas diariamente, revelam-se bastante sujeitas a erros que não são aceitáveis quando se faz o desenvolvimento de uma aplicação industrial. O Compilador de C/C++ parece que está cheio de erros pois a OpenRisc port usa pedaços de código emprestados de outras arquitecturas e foi desenvolvida para estar a funcionar no mais curto espaço de tempo. Por essa mesma razão é que a solução adoptada para resolver esta situação foi uma equipa de 4 elementos a trabalhar durante um mês produzir código sem qualquer ligação com o que existia na versão anterior. 8.5 Melhorias observadas pelo uso das macros De seguida apresenta-se uma série de resultados que foram obtidos pelo uso de macros em detrimento das funções usadas pela norma do codec. Os resultados aqui apresentados foram obtidos usado a sequência de teste já descrita anteriormente. Esta faz uma mistura de todos os modos do codec. Por isto, os valores apresentados espelham valores médios. Nunca valores para os quais realmente deverá ser dimensionado o valor de funcionamento do relógio do CPU, que corresponde ao pior caso de funcionamento do codec. Aqui pretende-se apenas ter uma ideia da percentagem que se ganha com o uso de macros no peso computacional do codec. A simulação do codec tal como vem definida pela norma (sem a chamada às funções de profiling) revelou os seguintes dados antes e depois do uso das macros.

Hardware/Software co-design para aplicações de processamento de voz 126 Número de instruções obtidos da simulação Sem macros Com macros Codificador 1744054205 755573265 Descodificador 301430481 138492404 Tabela 23: Número de instruções obtido da simulação para o codificador e descodificador MIPS Sem macros Com macros Codificador 205,2 88,9 Descodificador 35,5 16,3 Tabela 24: MIPS médio para codificador e descodificador A primeira tabela indica o número de instruções que foram realizadas em toda a simulação. Como sabemos o número de frames processadas (425) e a duração de cada uma (20 ms), podemos estimar um valor médio do número de MIPS necessários à execução do codec. Estes valores são apresentados na segunda tabela. Como se pode concluir pelos valores apresentados pelas tabelas, existe uma redução muito grande do valor de processamento médio necessário antes e depois do uso de macros. No codificador tivemos uma redução de 2.30 vezes e no descodificador de 2.18 vezes. De salientar que as funções básicas não foram todas implementadas pelo que estes valores ainda poderão ser melhores. Conclui-se que o uso de macros revela ser eficiente.

Hardware/Software co-design para aplicações de processamento de voz 127 9 Perspectivas de desenvolvimento futuras Este trabalho, infelizmente, não conseguiu chegar ao seu final. Um dos principais objectivos para se prosseguir com ele é, sem dúvida alguma, a implementação de instruções dedicadas no processador de forma a se conseguir optimizar ao máximo o código do codec de voz AMR. No entanto, a continuação deste trabalho só deve ser considerada se se tiver garantias que o compilador funciona correctamente. 9.1 Operações básicas a serem implementadas na arquitectura do OPENRISC1200 Da análise feita ao código pode-se dizer que as instruções dedicadas mais importantes a serem implementadas no processador OR1k são as de leitura do valor do acumulador da mac. Isto porque estas mostraram-se muito complicadas (extensas) de serem realizadas em assembly. Isto torna-se particularmente ineficiente quando estas funções têm de estar constantemente a ser chamadas dentro dum ciclo for. Esta situação ocorre muitas vezes no código, em especial a associação da operação de mac com a operação round. Esta seria uma optimização muito importante a ser realizada. As operações de shift left também se demonstraram muito ineficientes quando têm que ser chamadas para garantir a saturação do seu valor de saída. Funções com saturação de multiplicação e adição/subtracção também dariam bons resultados. 9.2 32 bits versus 16 bits Um dos grandes problemas da norma do codec é que esta está feita para processadores de 16 bits e o processador que se dispõem é de 32 bits. Uma questão que à partida pode ser logo levantada é o facto de estarmos a garantir operações com saturação em operandos (registos do processador) de 32 bits. Seria uma solução perfeitamente aceitável não se fazer saturação para variáveis de 16 bits. A questão é que a maioria das operações está definida como tendo parâmetros de entrada variáveis de

Hardware/Software co-design para aplicações de processamento de voz 128 16 bits. Esta solução levaria a ter-se que pensar numa estratégia para reescrever as operações básicas necessárias. Depois de feito isto, teríamos que declarar as variáveis do código para 32 bits. Aqui surgiria um grande problema. É que todos os dados existentes ao longo do código estão em 16 bits. Ou seja, a memória de dados iria duplicar e metade dos dados que depois iriam ser lidos da memória eram lixo. Isto não pode acontecer. Poderíamos então agrupar duas variáveis de 16 bits numa de 32 bits Em suma, muito provavelmente teríamos que reescreve o código todo para se evitar o uso da saturação em variáveis de 16 bits. Uma das principais ineficiências ao nível do código gerado para o processador, com que nos deparamos ao longo da execução deste trabalho foi a questão dos casts que são realizadas de termos um processador de 32 bits e muitas vezes usarmos variáveis de 16 bits. Como estamos a fazer a saturação das variáveis, os casts tornam-se redundantes. Para se ter uma ideia da ineficiência associada a esta situação, fez-se o seguinte teste. Foi-se ao código inicial da norma (sem o uso das macros) e declaram-se todas as variáveis como sendo de 32 bits. Obteve-se agora uma redução de 1.2 vezes no tempo de processamento face à situação onde existiam variáveis de 16 bits. Desconhece-se uma forma que evite que o compilador realize casts das variáveis de 32 bits para 16 bits.

Hardware/Software co-design para aplicações de processamento de voz 129 10 Conclusões A realização deste trabalho de acordo com os objectivos inicialmente propostos revelou-se incompleta. Convém referir todavia, que os objectivos deste trabalho eram demasiado ambiciosos para o tempo disponível. Foram tantas as pesquisas que se tiveram de fazer, quer resultantes por tentativas falhadas quer por soluções a implementar, que a filtragem da informação se torna bastante complicada. O principal obstáculo encontrado ao longo da execução do trabalho foi a escassez de documentação relativa ao correcto manuseamento das ferramentas que auxiliam o processador Or1k. Estas, muitas das vezes, eram mesmo inexistentes. Tal situação resultou num tempo gasto para se conseguir trabalhar com tais ferramentas demasiado longo. A própria familiarização com as ferramentas usadas e o próprio ambiente onde estão integradas foi lenta, o que se traduziu num consumo temporal elevado. Por mais complexas que fossem as tentativas para solucionar o não funcionamento de uma das ferramentas no Cygwin, elas foram infrutíferas e revelaram-se gastadoras de tempo útil. A norma do codec AMR revelou-se bem documentada e não apresentou qualquer tipo de problemas de raiz ao nível do seu código C. Esta última encontrava-se bem estruturada e, por isso mesmo, pronta a ser adaptada a uma dada arquitectura de um processador. Tentativas de optimizar o algoritmo da norma do codec para se obter melhoramentos a nível de recursos gastos em processamento, sem degradar a qualidade do codec, foram alcançadas com sucesso no seu modo de funcionamento de maior débito. Aqui, obteve-se reduções de 15.2% da capacidade máxima de processamento. O uso do compilador que é fornecido pelo processador revelou ser instável. Testes realizados ao longo do trabalho deram a entender que, aquando da criação do compilador, não houve cuidados em se optimizar o compilador à arquitectura do processador em toda a sua plenitude. Crê-se mesmo que este compilador tenha sido resultado de uma reunião de vários compiladores de outros processadores. O uso de macros para se optimizar as operações básicas definidas pela norma do codec revelou ser bastante eficiente. No entanto, esta fase do trabalho não foi completada devido a erros que foram surgindo devido à compilação do programa do codec e à excessiva extensão deste.

Hardware/Software co-design para aplicações de processamento de voz 130 É ainda de salientar que os vários cores que existem disponíveis de forma livre em www.opencores.org, permitem que se possa construir um sistema elaborado e que surge como a solução de pressupostos bem definidos. No entanto, existe sempre a limitação a nível de documentação.

Hardware/Software co-design para aplicações de processamento de voz 131 11 Bibliografia Goldberg, R. G. A Practical Handbook of Speech Coders CRC Press 2000 http://www.3gpp.org/ftp/specs/html-info/26071.htm - AMR speech Codec; General description; version 6.0.0 http://www.3gpp.org/ftp/specs/html-info/26073.htm - AMR speech Codec; C- source code; version 6.0.0 http://www.3gpp.org/ftp/specs/html-info/26074.htm - AMR speech Codec; Test sequences; version 6.0.0 http://www.3gpp.org/ftp/specs/html-info/26090.htm - AMR speech Codec; Transcoding Functions; version 6.0.0 http://www.3gpp.org/ftp/specs/html-info/26093.htm - AMR speech Codec; Source Controlled Rate operation; version 6.0.0 http://www.3gpp.org/ftp/specs/html-info/26094.htm - AMR Speech Codec; Voice Activity Detector for AMR Speech Traffic Channels; version 6.0.0 http://www.opencores.org/projects/mem_ctrl/ http://www.opencores.org/projects/or1k/ http://www.opencores.org/projects/wb_builder/ ITU-T Rec. G.711: "Pulse code modulation (PCM) of voice frequencies". ITU-T Rec. G.729: Coding of Speech at 8 kbit/s Using Conjugate-Structure Algrbraic Code-Excited Linear-PRediction (CS-ACELP). M.Bolado, J. Castillo : Using Open Source Cores in Real Applications, DCIS 2003 Opencores Project OpenRisc 1000 Redwan Salami, Claude Laflamme, Bruno Bessette, ITU-T G.729 Annex A: Reduced Complexity 8 kb/s CS-ACELP Codec for Digital Simultaneous Voice and Data, IEEE Communications Magazine, September 1997.

Hardware/Software co-design para aplicações de processamento de voz 132 Anexo 1:Versão simplificada do algoritmo do codificador ficheiro s10_8pf.c /* ******************************************************************************** * * GSM AMR-NB speech codec R98 Version 7.6.0 December 12, 2001 * R99 Version 3.3.0 * REL-4 Version 4.1.0 * * Versão simplificada * ------------------- * Pedro Santos * Pedro Mota Maio de 2005 * ******************************************************************************** * * File : s10_8pf.c * Purpose : Searches a 35/31 bit algebraic codebook containing * : 10/8 pulses in a frame of 40 samples. * ******************************************************************************** */ /* ******************************************************************************** * MODULE INCLUDE FILE AND VERSION ID ******************************************************************************** */ #include "s10_8pf.h" const char s10_8pf_id[] = "@(#)$Id $" s10_8pf_h; /* ******************************************************************************** * INCLUDE FILES ******************************************************************************** */ #include <stdio.h> #include <stdlib.h> #include "typedef.h" #include "basic_op.h" #include "count.h" #include "cnst.h" /* ******************************************************************************** * LOCAL VARIABLES AND TABLES ******************************************************************************** */ /************************************************************************* * * FUNCTION search_10and8i40() * * PURPOSE: Search the best codevector; determine positions of the 10/8 * pulses in the 40-sample frame. * * search_10and8i40 (10,5,5,dn, rr, ipos, pos_max, codvec); for GSMEFR * search_10and8i40 (8, 4,4,dn, rr, ipos, pos_max, codvec); for 10.2 * *************************************************************************/ #define _1_2 (Word16)(32768L/2) #define _1_4 (Word16)(32768L/4) #define _1_8 (Word16)(32768L/8) #define _1_16 (Word16)(32768L/16) #define _1_32 (Word16)(32768L/32) #define _1_64 (Word16)(32768L/64) #define _1_128 (Word16)(32768L/128) void search_10and8i40 ( Word16 nbpulse, /* i : nbpulses to find */ Word16 step, /* i : stepsize */ Word16 nbtracks, /* i : nbtracks */ Word16 dn[], /* i : correlation between target and h[] */ Word16 rr[][l_code], /* i : matrix of autocorrelation */ Word16 ipos[], /* i : starting position for each pulse */ Word16 pos_max[], /* i : position of maximum of dn[] */

Hardware/Software co-design para aplicações de processamento de voz 133 ) { Word16 codvec[] /* o : algebraic codebook vector */ Word16 i0, i1, i2, i3, i4, i5, i6, i7, i8=0, i9; Word16 i, j, k, pos, ia; Word16 psk, ps, ps0, ps1, sq, sq1; Word16 alpk, alp, alp_16; Word32 s, alp0, alp1; Word16 gsmefrflag; test(); if (sub(nbpulse, 10) == 0) { gsmefrflag=1; move16 (); else { gsmefrflag=0; move16 (); /* fix i0 on maximum of correlation position */ i0 = pos_max[ipos[0]]; move16 (); /*------------------------------------------------------------------* * i1 loop: * *------------------------------------------------------------------*/ /* Default value */ psk = -1; move16 (); alpk = 1; move16 (); for (i = 0; i < nbpulse; i++) { codvec[i] = i; move16 (); for (i = 1; i < nbtracks; i++) { i1 = pos_max[ipos[1]]; move16 (); ps0 = add (dn[i0], dn[i1]); alp0 = L_mult (rr[i0][i0], _1_16); alp0 = L_mac (alp0, rr[i1][i1], _1_16); alp0 = L_mac (alp0, rr[i0][i1], _1_8); /*----------------------------------------------------------------* * i2 loop: * *----------------------------------------------------------------*/ /* initialize 4 indices for i2 loop. */ move16 (); /* initialize "dn[i2]" pointer */ move16 (); /* initialize "rr[i2][i2]" pointer */ move16 (); /* initialize "rr[i0][i2]" pointer */ move16 (); /* initialize "rr[i1][i2]" pointer */ /* Default value */ sq = -1; move16 (); alp = 1; move16 (); ps = 0; move16 (); ia = ipos[2]; move16 (); for (i2 = ipos[2]; i2 < L_CODE; i2 += step) { /* index increment = step */ ps1 = add (ps0, dn[i2]); /* index incr= step+l_code */ alp1 = L_mac (alp0, rr[i2][i2], _1_16); /* index increment = step */ alp1 = L_mac (alp1, rr[i0][i2], _1_8); /* index increment = step */ alp1 = L_mac (alp1, rr[i1][i2], _1_8); sq1 = mult (ps1, ps1); alp_16 = round (alp1); s = L_msu (L_mult (alp, sq1), sq, alp_16);

Hardware/Software co-design para aplicações de processamento de voz 134 test (); if (s > 0) { sq = sq1; move16 (); ps = ps1; move16 (); alp = alp_16; move16 (); ia = i2; move16 (); i2 = ia; move16 (); /*----------------------------------------------------------------* * i3 loop: * *----------------------------------------------------------------*/ ps0 = ps; move16 (); /*alp0 = L_mult (alp, _1_1);*/ alp0 = L_deposit_h (alp); /* Default value */ sq = -1; move16 (); alp = 1; move16 (); ps = 0; move16 (); ia = ipos[3]; move16 (); /* initialize 5 indices for i3 loop (see i2 loop) */ move16 (); move16 (); move16 (); move16 (); move16 (); for (i3 = ipos[3]; i3 < L_CODE; i3 += step) { /* index increment = step */ ps1 = add (ps0, dn[i3]); alp1 = L_mac (alp0, rr[i3][i3], _1_16); /* index incr= step+l_code */ alp1 = L_mac (alp1, rr[i0][i3], _1_8); /* index increment = step */ alp1 = L_mac (alp1, rr[i1][i3], _1_8); /* index increment = step */ alp1 = L_mac (alp1, rr[i2][i3], _1_8); sq1 = mult (ps1, ps1); alp_16 = round (alp1); s = L_msu (L_mult (alp, sq1), sq, alp_16); test (); if (s > 0) { sq = sq1; move16 (); ps = ps1; move16 (); alp = alp_16; move16 (); ia = i3; move16 (); i3 = ia; move16 (); /*----------------------------------------------------------------* * i4 loop: * *----------------------------------------------------------------*/ ps0 = ps; move16 (); alp0 = L_mult (alp, _1_2); /* Default value */ sq = -1; move16 (); alp = 1; move16 (); ps = 0; move16 (); ia = ipos[4]; move16 (); /* initialize 6 indices for i4 loop (see i2 loop) */ move16 (); move16 (); move16 (); move16 (); move16 (); move16 (); for (i4 = ipos[4]; i4 < L_CODE; i4 += step) { /* index increment = step */ ps1 = add (ps0, dn[i4]); alp1 = L_mac (alp0, rr[i4][i4], _1_32);

Hardware/Software co-design para aplicações de processamento de voz 135 alp1 = L_mac (alp1, rr[i0][i4], _1_16); alp1 = L_mac (alp1, rr[i1][i4], _1_16); alp1 = L_mac (alp1, rr[i2][i4], _1_16); alp1 = L_mac (alp1, rr[i3][i4], _1_16); sq1 = mult (ps1, ps1); alp_16 = round (alp1); s = L_msu (L_mult (alp, sq1), sq, alp_16); test (); if (s > 0) { sq = sq1; move16 (); ps = ps1; move16 (); alp = alp_16; move16 (); ia = i4; move16 (); i4 = ia; move16 (); /*----------------------------------------------------------------* * i5 loop: * *----------------------------------------------------------------*/ ps0 = ps; move16 (); /*alp0 = L_mult (alp, _1_1);*/ alp0 = L_deposit_h (alp); /* Default value */ sq = -1; move16 (); alp = 1; move16 (); ps = 0; move16 (); ia = ipos[5]; move16 (); /* initialize 7 indices for i5 loop (see i2 loop) */ move16 (); move16 (); move16 (); move16 (); move16 (), move16 (), move16 (); for (i5 = ipos[5]; i5 < L_CODE; i5 += step) { /* index increment = step */ ps1 = add (ps0, dn[i5]); alp1 = L_mac (alp0, rr[i5][i5], _1_32); alp1 = L_mac (alp1, rr[i0][i5], _1_16); alp1 = L_mac (alp1, rr[i1][i5], _1_16); alp1 = L_mac (alp1, rr[i2][i5], _1_16); alp1 = L_mac (alp1, rr[i3][i5], _1_16); alp1 = L_mac (alp1, rr[i4][i5], _1_16); sq1 = mult (ps1, ps1); alp_16 = round (alp1); s = L_msu (L_mult (alp, sq1), sq, alp_16); test (); if (s > 0) { sq = sq1; move16 (); ps = ps1; move16 (); alp = alp_16; move16 (); ia = i5; move16 (); i5 = ia; move16 (); /*----------------------------------------------------------------* * i6 loop: * *----------------------------------------------------------------*/ ps0 = ps; move16 (); alp0 = L_mult (alp, _1_2); /* Default value */

Hardware/Software co-design para aplicações de processamento de voz 136 sq = -1; move16 (); alp = 1; move16 (); ps = 0; move16 (); ia = ipos[6]; move16 (); /* initialize 8 indices for i6 loop (see i2 loop) */ move16 (); move16 (); move16 (); move16 (); move16 (), move16 (), move16 (); move16 (); for (i6 = ipos[6]; i6 < L_CODE; i6 += step) { /* index increment = step */ ps1 = add (ps0, dn[i6]); alp1 = L_mac (alp0, rr[i6][i6], _1_64); alp1 = L_mac (alp1, rr[i0][i6], _1_32); alp1 = L_mac (alp1, rr[i1][i6], _1_32); alp1 = L_mac (alp1, rr[i2][i6], _1_32); alp1 = L_mac (alp1, rr[i3][i6], _1_32); alp1 = L_mac (alp1, rr[i4][i6], _1_32); alp1 = L_mac (alp1, rr[i5][i6], _1_32); sq1 = mult (ps1, ps1); alp_16 = round (alp1); s = L_msu (L_mult (alp, sq1), sq, alp_16); test (); if (s > 0) { sq = sq1; move16 (); ps = ps1; move16 (); alp = alp_16; move16 (); ia = i6; move16 (); i6 = ia; move16 (); /*----------------------------------------------------------------* * i7 loop: * *----------------------------------------------------------------*/ ps0 = ps; move16 (); /*alp0 = L_mult (alp, _1_1);*/ alp0 = L_deposit_h (alp); /* Default value */ sq = -1; move16 (); alp = 1; move16 (); ps = 0; move16 (); ia = ipos[7]; move16 (); /* initialize 9 indices for i7 loop (see i2 loop) */ move16 (); move16 (); move16 (); move16 (); move16 (); move16 (), move16 (), move16 (); move16 (); for (i7 = ipos[7]; i7 < L_CODE; i7 += step) { /* index increment = step */ ps1 = add (ps0, dn[i7]); alp1 = L_mac (alp0, rr[i7][i7], _1_64); alp1 = L_mac (alp1, rr[i0][i7], _1_32); alp1 = L_mac (alp1, rr[i1][i7], _1_32); alp1 = L_mac (alp1, rr[i2][i7], _1_32); alp1 = L_mac (alp1, rr[i3][i7], _1_32); alp1 = L_mac (alp1, rr[i4][i7], _1_32); alp1 = L_mac (alp1, rr[i5][i7], _1_32); alp1 = L_mac (alp1, rr[i6][i7], _1_32); sq1 = mult (ps1, ps1); alp_16 = round (alp1); s = L_msu (L_mult (alp, sq1), sq, alp_16);

Hardware/Software co-design para aplicações de processamento de voz 137 test (); if (s > 0) { sq = sq1; move16 (); ps = ps1; move16 (); alp = alp_16; move16 (); ia = i7; move16 (); i7 = ia; move16 (); /* now finished searching a set of 8 pulses */ test(); if(gsmefrflag!= 0){ /* go on with the two last pulses for GSMEFR */ /*----------------------------------------------------------------* * i8 loop: * *----------------------------------------------------------------*/ ps0 = ps; move16 (); alp0 = L_mult (alp, _1_2); /* Default value */ sq = -1; move16 (); alp = 1; move16 (); ps = 0; move16 (); ia = ipos[8]; move16 (); /* initialize 10 indices for i8 loop (see i2 loop) */ move16 (); move16 (); move16 (); move16 (); move16 (); move16 (), move16 (), move16 (); move16 (); move16 (); for (i8 = ipos[8]; i8 < L_CODE; i8 += step) { /* index increment = step */ ps1 = add (ps0, dn[i8]); alp1 = L_mac (alp0, rr[i8][i8], _1_128); alp1 = L_mac (alp1, rr[i0][i8], _1_64); alp1 = L_mac (alp1, rr[i1][i8], _1_64); alp1 = L_mac (alp1, rr[i2][i8], _1_64); alp1 = L_mac (alp1, rr[i3][i8], _1_64); alp1 = L_mac (alp1, rr[i4][i8], _1_64); alp1 = L_mac (alp1, rr[i5][i8], _1_64); alp1 = L_mac (alp1, rr[i6][i8], _1_64); alp1 = L_mac (alp1, rr[i7][i8], _1_64); sq1 = mult (ps1, ps1); alp_16 = round (alp1); s = L_msu (L_mult (alp, sq1), sq, alp_16); test (); if (s > 0) { sq = sq1; move16 (); ps = ps1; move16 (); alp = alp_16; move16 (); ia = i8; move16 (); i8 = ia; move16 (); /*----------------------------------------------------------------* * i9 loop: * *----------------------------------------------------------------*/ ps0 = ps; move16 (); /*alp0 = L_mult (alp, _1_1);*/ alp0 = L_deposit_h (alp); /* Default value */ sq = -1; move16 (); alp = 1; move16 (); ps = 0; move16 ();

Hardware/Software co-design para aplicações de processamento de voz 138 ia = ipos[9]; move16 (); /* initialize 11 indices for i9 loop (see i2 loop) */ move16 (); move16 (); move16 (); move16 (); move16 (); move16 (); move16 (), move16 (), move16 (); move16 (); move16 (); for (i9 = ipos[9]; i9 < L_CODE; i9 += step) { /* index increment = step */ ps1 = add (ps0, dn[i9]); alp1 = L_mac (alp0, rr[i9][i9], _1_128); alp1 = L_mac (alp1, rr[i0][i9], _1_64); alp1 = L_mac (alp1, rr[i1][i9], _1_64); alp1 = L_mac (alp1, rr[i2][i9], _1_64); alp1 = L_mac (alp1, rr[i3][i9], _1_64); alp1 = L_mac (alp1, rr[i4][i9], _1_64); alp1 = L_mac (alp1, rr[i5][i9], _1_64); alp1 = L_mac (alp1, rr[i6][i9], _1_64); alp1 = L_mac (alp1, rr[i7][i9], _1_64); alp1 = L_mac (alp1, rr[i8][i9], _1_64); sq1 = mult (ps1, ps1); alp_16 = round (alp1); s = L_msu (L_mult (alp, sq1), sq, alp_16); test (); if (s > 0) { sq = sq1; move16 (); ps = ps1; move16 (); alp = alp_16; move16 (); ia = i9; move16 (); /* end gsmefrflag */ /*---------------------------------------------------------------- * * test and memorise if this combination is better than the last one.* *----------------------------------------------------------------*/ s = L_msu (L_mult (alpk, sq), psk, alp); test (); if (s > 0) { psk = sq; move16 (); alpk = alp; move16 (); codvec[0] = i0; move16 (); codvec[1] = i1; move16 (); codvec[2] = i2; move16 (); codvec[3] = i3; move16 (); codvec[4] = i4; move16 (); codvec[5] = i5; move16 (); codvec[6] = i6; move16 (); codvec[7] = i7; move16 (); test(); if (gsmefrflag!= 0) { codvec[8] = i8; move16 (); codvec[9] = ia; move16 (); /*----------------------------------------------------------------* * Cyclic permutation of i1,i2,i3,i4,i5,i6,i7,(i8 and i9). * *----------------------------------------------------------------*/ pos = ipos[1]; move16 (); for (j = 1, k = 2; k < nbpulse; j++, k++) { ipos[j] = ipos[k]; move16 (); ipos[sub(nbpulse,1)] = pos; move16 (); /* end 1..nbTracks loop*/

Hardware/Software co-design para aplicações de processamento de voz 139 Anexo 2:Contagem das operações básicas da norma do codec ficheiro count.c /*********************************************************************** * * This file contains functions for the automatic complexity calculation * $Id $ * * Versão modificada: Contagem das operações básicas * Pedro Santos * Pedro Mota * *************************************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include "typedef.h" #include "count.h" /* Global counter variable for calculation of complexity weight */ BASIC_OP multicounter[maxcounters]; int currcounter=0; /* Zero equals global counter */ /*BASIC_OP counter;*/ const BASIC_OP op_weight = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 4, 15, 18, 30, 1, 2, 1, 2, 2 ; const char name[][50] = { "add", /* Complexity Weight of 1 */ "sub", "abs_s", "shl", "shr", "extract_h", "extract_l", "mult", "L_mult", "negate", "round", "L_mac", "L_msu", "L_macNs", "L_msuNs", "L_add", /* Complexity Weight of 2 */ "L_sub", "L_add_c", "L_sub_c", "L_negate", "L_shl", "L_shr", "mult_r", "shr_r", "shift_r", "mac_r", "msu_r", "L_deposit_h", "L_deposit_l", "L_shr_r", /* Complexity Weight of 3 */ "L_shift_r", "L_abs", "L_sat", /* Complexity Weight of 4 */ "norm_s", /* Complexity Weight of 15 */ "div_s", /* Complexity Weight of 18 */ "norm_l", /* Complexity Weight of 30 */ "DataMove16", /* Complexity Weight of 1 */ "DataMove32", /* Complexity Weight of 2 */ "Logic16", /* Complexity Weight of 1 */ "Logic32", /* Complexity Weight of 2 */ "Test" /* Complexity Weight of 2 */

Hardware/Software co-design para aplicações de processamento de voz 140 ; /* function prototypes */ Word32 TotalWeightedOperation (void); Word32 DeltaWeightedOperation (void); /* local variable */ #if WMOPS /* Counters for separating counting for different objects */ static int maxcounter=0; static char* objectname[maxcounters+1]; static Word16 fwc_corr[maxcounters+1]; #define NbFuncMax 1024 static Word16 funcid[maxcounters], nbframe[maxcounters]; static Word32 glob_wc[maxcounters], wc[maxcounters][nbfuncmax]; static float total_wmops[maxcounters]; /*--------------------------------------------------------------*/ static Word32 total_wmops_operations[maxcounters][nbfuncmax]; /*--------------------------------------------------------------*/ static Word32 LastWOper[MAXCOUNTERS]; static char* my_strdup(const char *s) /* * duplicates UNIX function strdup() which is not ANSI standard: * -- malloc() memory area big enough to hold the string s * -- copy string into new area * -- return pointer to new area * * returns NULL if either s==null or malloc() fails */ { char *dup; if (s == NULL) return NULL; /* allocate memory for copy of ID string (including string terminator) */ /* NOTE: the ID strings will never be deallocated because there is no way to "destroy" a counter that is not longer needed */ if ((dup = (char *) malloc(strlen(s)+1)) == NULL) return NULL; return strcpy(dup, s); #endif int getcounterid(char *objectnamearg) { #if WMOPS if(maxcounter>=maxcounters-1) return 0; objectname[++maxcounter]=my_strdup(objectnamearg); return maxcounter; #else return 0; /* Dummy */ #endif void setcounter(int counterid) { #if WMOPS if(counterid>maxcounter counterid<0) { currcounter=0;

Hardware/Software co-design para aplicações de processamento de voz 141 return; currcounter=counterid; #endif #if WMOPS static Word32 WMOPS_frameStat() /* calculate the WMOPS seen so far and update the global per-frame maximum (glob_wc) */ { Word32 tot; tot = TotalWeightedOperation (); if (tot > glob_wc[currcounter]) glob_wc[currcounter] = tot; /* check if fwc() was forgotten at end of last frame */ if (tot > LastWOper[currCounter]) { if (!fwc_corr[currcounter]) { fprintf(stderr, "count: operations counted after last fwc() for '%s'; " "-> fwc() called\n", objectname[currcounter]?objectname[currcounter]:""); fwc(); return tot; static void WMOPS_clearMultiCounter() { Word16 i; Word32 *ptr = (Word32 *) &multicounter[currcounter]; for (i = 0; i < (sizeof (multicounter[currcounter])/ sizeof (Word32)); i++) { *ptr++ = 0; #endif Word32 TotalWeightedOperation () { #if WMOPS Word16 i; Word32 tot, *ptr, *ptr2; tot = 0; ptr = (Word32 *) &multicounter[currcounter]; ptr2 = (Word32 *) &op_weight; for (i = 0; i < (sizeof (multicounter[currcounter])/ sizeof (Word32)); i++) { tot += ((*ptr++) * (*ptr2++)); return ((Word32) tot); #else return 0; /* Dummy */ #endif Word32 DeltaWeightedOperation () { #if WMOPS Word32 NewWOper, delta; NewWOper = TotalWeightedOperation (); delta = NewWOper - LastWOper[currCounter]; LastWOper[currCounter] = NewWOper; return (delta); #else return 0; /* Dummy */ #endif

Hardware/Software co-design para aplicações de processamento de voz 142 void move16 (void) { #if WMOPS multicounter[currcounter].datamove16++; #endif void move32 (void) { #if WMOPS multicounter[currcounter].datamove32++; #endif void test (void) { #if WMOPS multicounter[currcounter].test++; #endif void logic16 (void) { #if WMOPS multicounter[currcounter].logic16++; #endif void logic32 (void) { #if WMOPS multicounter[currcounter].logic32++; #endif void Init_WMOPS_counter (void) { #if WMOPS Word16 i; /* reset function weight operation counter variable */ for (i = 0; i < NbFuncMax; i++){ wc[currcounter][i] = (Word32) 0; total_wmops_operations[currcounter][i] = (Word32)0; glob_wc[currcounter] = 0; nbframe[currcounter] = 0; total_wmops[currcounter] = 0.0; /* initially clear all counters */ WMOPS_clearMultiCounter(); LastWOper[currCounter] = 0; funcid[currcounter] = 0; #endif void Reset_WMOPS_counter (void) { #if WMOPS Word32 tot = WMOPS_frameStat(); Word16 i; Word32 *ptr = (Word32 *) &multicounter[currcounter]; /* increase the frame counter --> a frame is counted WHEN IT BEGINS */ nbframe[currcounter]++; /* add wmops used in last frame to count, then reset counter */ /* (in first frame, this is a no-op */ total_wmops[currcounter] += ((float) tot) * 0.00005;

Hardware/Software co-design para aplicações de processamento de voz 143 /* clear counter before new frame starts */ /* WMOPS_clearMultiCounter();*/ LastWOper[currCounter] = 0; funcid[currcounter] = 0; /* new frame, set function id to zero */ for(i = 0; i < (sizeof (multicounter[currcounter])/ sizeof (Word32)); i++){ total_wmops_operations[currcounter][i] += *ptr++; WMOPS_clearMultiCounter(); #endif Word32 fwc (void) /* function worst case */ { #if WMOPS Word32 tot; tot = DeltaWeightedOperation (); if (tot > wc[currcounter][funcid[currcounter]]) wc[currcounter][funcid[currcounter]] = tot; funcid[currcounter]++; return (tot); #else return 0; /* Dummy */ #endif void WMOPS_output (Word16 dtx_mode) { #if WMOPS Word16 i; Word32 tot, tot_wm, tot_wc; Word32 *ptr = (Word32 *) &multicounter[currcounter]; FILE *fich = fopen("wmops.txt","a"); /* get operations since last reset (or init), but do not update the counters (except the glob_wc[] maximum) so output CAN be called in each frame without problems. The frame counter is NOT updated! */ tot = WMOPS_frameStat(); tot_wm = total_wmops[currcounter] + ((float) tot) * 0.00005; fprintf (stderr, "%10s:WMOPS=%.3f", objectname[currcounter]?objectname[currcounter]:"", ((float) tot) * 0.00005); fprintf (fich, "%10s:WMOPS=%.3f", objectname[currcounter]?objectname[currcounter]:"", ((float) tot) * 0.00005); if (nbframe[currcounter]!= 0){ fprintf (stderr, " Average=%.3f", tot_wm / (float) nbframe[currcounter]); fprintf (fich, " Average=%.3f", tot_wm / (float) nbframe[currcounter]); fprintf (stderr, " WorstCase=%.3f", ((float) glob_wc[currcounter]) * 0.00005); fprintf (fich, " WorstCase=%.3f", ((float) glob_wc[currcounter]) * 0.00005); /* Worst worst case printed only when not in DTX mode */

Hardware/Software co-design para aplicações de processamento de voz 144 if (dtx_mode == 0) { tot_wc = 0L; for (i = 0; i < funcid[currcounter]; i++) tot_wc += wc[currcounter][i]; fprintf (stderr, " WorstWC=%.3f", ((float) tot_wc) * 0.00005); fprintf (fich, " WorstWC=%.3f", ((float) tot_wc) * 0.00005); fprintf (stderr, " (%d frames)\n", nbframe[currcounter]); fprintf (fich, " (%d frames)\n", nbframe[currcounter]); for(i = 0; i < (sizeof (multicounter[currcounter])/ sizeof (Word32)); i++){ total_wmops_operations[currcounter][i] += *ptr++; if (nbframe[currcounter]!= 0){ fprintf (stderr, "%10s:\n", objectname[currcounter]?objectname[currcounter]:"\n"); for(i = 0; i < (sizeof (multicounter[currcounter])/ sizeof (Word32)); i++){ fprintf (stderr, " Instrução %s \t -> Average=%.0f\n", name[i], total_wmops_operations[currcounter][i] / (float) nbframe[currcounter]); fprintf (fich, " Instrução %s \t -> Average=%.0f\n", name[i], total_wmops_operations[currcounter][i] / (float) nbframe[currcounter]); fprintf (stderr, " (%d frames)\n\n", nbframe[currcounter]); fprintf (fich, " (%d frames)\n\n", nbframe[currcounter]); fclose(fich); #endif

Hardware/Software co-design para aplicações de processamento de voz 145 Anexo 3: Implementação da lei A em código C /* ******************************************************************************** * * A-LAW (ITU-G711) * * a_law.h * * Pedro Santos - ee00115@fe.up.pt * Pedro Mota - ee00022@fe.up.pt * * April 2005 * ******************************************************************************** */ /* ******************************************************************************** * * Function: linear2alaw * * Purpose: A-law encoder. * * Descrition: an 16-bit integer and encodes it as A-law data. * ******************************************************************************** */ void linear2alaw( Word16 pcm_val[], /* (i) 2's complement (16-bit range) */ Word8 a_law[], /* (o) a law */ Word16 ld /* (i) number of samples */ ); /* ******************************************************************************** * * Function: alaw2linear * * Purpose: A-law decoder. * * Descrition: Convert an A-law value to 16-bit linear PCM. * ******************************************************************************** */ void alaw2linear( Word8 a_law[], /* (i) a law */ Word16 pcm_val[], /* (o) 2's complement (16-bit range) */ Word16 ld ); /* (i) number of samples */

Hardware/Software co-design para aplicações de processamento de voz 146 /* ******************************************************************************** * * A-LAW (ITU-G711) * * a_law.c * * Pedro Santos - ee00115@fe.up.pt * Pedro Mota - ee00022@fe.up.pt * * April 2005 * ******************************************************************************** */ typedef short Word16; typedef int Word32; typedef signed char Word8; #include "a_law.h" #define QUANT_MASK (0xf) /* Quantization field mask. */ #define SEG_SHIFT (4) /* Left shift for segment number. */ #define SEG_MASK (0x70) /* Segment field mask. */ #define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ /* ******************************************************************************** * LOCAL VARIABLES AND TABLES ******************************************************************************** */ static Word16 seg_aend[7] = {0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF/*, 0x0FFF*/; /* ******************************************************************************** * PUBLIC PROGRAM CODE ******************************************************************************** */ /* ******************************************************************************** * * Function: linear2alaw * * Purpose: A-law encoder. * * Descrition: an 16-bit integer and encodes it as A-law data. * * Algorith: * * Linear Input Code Compressed Code * ----------------- --------------- * 0000000wxyza 000wxyz * 0000001wxyza 001wxyz * 000001wxyzab 010wxyz * 00001wxyzabc 011wxyz * 0001wxyzabcd 100wxyz * 001wxyzabcde 101wxyz * 01wxyzabcdef 110wxyz * 1wxyzabcdefg 111wxyz * * For further information see John C. Bellamy's Digital Telephony, 1982, * John Wiley & Sons, pps 98-111 and 472-476. ******************************************************************************** */ void linear2alaw( Word16 pcm_val[], /* (i) 2's complement (16-bit range) */ Word8 a_law[], /* (o) a law */ Word16 ld) /* (i) number of samples */ { Word16 pcm_13; Word8 mask; Word8 seg; Word16 i;

Hardware/Software co-design para aplicações de processamento de voz 147 for(i = 0; i < ld; i++) { pcm_13 = pcm_val[i] >> 3; /* convert to 13 bits */ if (pcm_13 >= 0) { mask = 0xD5; /* sign (7th) bit = 1 */ else { mask = 0x55; /* sign (7th) bit = 0 */ pcm_13 = -pcm_13-1; /* Convert the scaled magnitude to segment number. */ for (seg = 0; seg < 7; seg++) { if (pcm_13 <= seg_aend[seg]) break; /* Combine the sign, segment, and quantization bits. */ a_law[i] = seg << SEG_SHIFT; if (seg < 2) a_law[i] = (Word8)(pcm_13 >> 1) & QUANT_MASK; else a_law[i] = (Word8)(pcm_13 >> seg) & QUANT_MASK; a_law[i] = a_law[i] ^ mask; /* ******************************************************************************** * * Function: alaw2linear * * Purpose: A-law decoder. * * Descrition: Convert an A-law value to 16-bit linear PCM. * * Algorith: * * Linear Input Code Compressed Code * ----------------- --------------- * 0000000wxyza 000wxyz * 0000001wxyza 001wxyz * 000001wxyzab 010wxyz * 00001wxyzabc 011wxyz * 0001wxyzabcd 100wxyz * 001wxyzabcde 101wxyz * 01wxyzabcdef 110wxyz * 1wxyzabcdefg 111wxyz * ******************************************************************************** */ void alaw2linear( Word8 a_law[], /* (i) a law */ Word16 pcm_val[], /* (o) 2's complement (16-bit range) */ Word16 ld ) /* (i) number of samples */ { Word16 t; Word8 a_val; Word8 seg; Word16 i; for(i = 0; i < ld; i++) { a_val = a_law[i]; a_val ^= 0x55; t = (a_val & QUANT_MASK) << 4; /* 4 por ser 16 bits, se fossem 13 bits seria 1 */ seg = (a_val & SEG_MASK) >> SEG_SHIFT; /* select segment */ if(seg){

Hardware/Software co-design para aplicações de processamento de voz 148 else t += 0x108; /* se a saida fosse de 13-bits seria 0x21 */ t <<= seg - 1; t += 8; /* se a saida fosse de 13-bits seria 1 */ if( a_val & SIGN_BIT ) pcm_val[i] = t; else pcm_val[i] = -t;

Hardware/Software co-design para aplicações de processamento de voz 149 /* ***************************************************************************** * decoder.c LEI A ***************************************************************************** */ typedef short Word16; typedef int Word32; typedef signed char Word8; #include "a_law.h" #include "speech.tab" #define L_FRAME 160 /* ***************************************************************************** * MAIN PROGRAM ***************************************************************************** */ int main (void) { Word8 *new_speech_a; /* Apontador para novo vector codificado */ Word16 speech_out[l_frame]; /* Vector com resultado da descodificação da frame acabada de processar */ Word32 frame; Word16 i,j; /*-----------------------------------------------------------------------* * Processa sinal frame por frame * *-----------------------------------------------------------------------*/ frame = 0; for (i=0; i<12; i++) { frame++; for(j=0; j<l_frame; j++) speech_out[j] = 0; new_speech_a = &sp_a[ (frame-1)*l_frame ]; alaw2linear(new_speech_a, speech_out, L_FRAME); for(j=0; j<l_frame; j++) { if( speech_out[j]!= sp_out[ (frame-1)*l_frame + j ] ) { report (i); exit (-1); report (i); exit (0);

Hardware/Software co-design para aplicações de processamento de voz 150 /* ***************************************************************************** * encoder.c LEI A ***************************************************************************** */ typedef short Word16; typedef int Word32; typedef signed char Word8; #include "a_law.h" #include "speech.tab" #include "support.h" #define L_FRAME 160 /* ***************************************************************************** * MAIN PROGRAM ***************************************************************************** */ int main (void) { Word16 *new_speech; /* Apontador para novo vector de fala */ Word8 speech_alaw[l_frame];/* Vector com a codificaï ½o da frame actual */ Word32 frame; Word16 i,j; /*-----------------------------------------------------------------------* * Processa sinal frame por frame * *-----------------------------------------------------------------------*/ frame = 0; for (i=0; i<12; i++) { frame++; for(j=0; j<l_frame; j++) speech_alaw[j] = 0; new_speech = &sp[ (frame-1)*l_frame ]; linear2alaw(new_speech, speech_alaw, L_FRAME); for(j=0; j<l_frame; j++) { if( speech_alaw[j]!= sp_a[ (frame-1)*l_frame + j ] ) { report (i); exit (-1); report (i); exit (0);

Hardware/Software co-design para aplicações de processamento de voz 151 # makefile lei A objects=except.o a_law.o encoder.o obj_clean=encoder a_law.o encoder.o CC=or32-uclinux-gcc FLAGS_2=-Wall -g -nostdlib -mhard-div FLAGS_1=-I. -I/home/pedro/or1k/or1ksim/testbench/support SUPPORTLIBS=/home/pedro/or1k/or1ksim/testbench/support/libsupport.a LD_FLAGS= -nostdlib $(SUPPORTLIBS) encoder : $(objects) $(CC) -o encoder -T.//default.ld $(objects) $(LD_FLAGS) a_law.o : a_law.c a_law.h $(CC) -c $(FLAGS_1) $(FLAGS_2) a_law.c encoder.o: encoder.c a_law.h $(CC) -c $(FLAGS_1) $(FLAGS_2) encoder.c clean : $(obj_clean) rm -f $(obj_clean)

Hardware/Software co-design para aplicações de processamento de voz 152 Anexo 4:Top level do CPU

Hardware/Software co-design para aplicações de processamento de voz 153 Anexo 5: Instruções de vírgula fixa do processador Or1k Arithmetic Instructions Instruction: Description: 32-bit implementation: Exceptions: l.add rd, ra, rb Add Signed rd[31:0] < - ra[31:0] + rb[31:0] Range Exception SR[CY] < - carry SR[OV] < - overflow l.addc rd,ra,rb Add Signed and Carry rd[31:0] < - ra[31:0] + rb[31:0] + SR[CY] Range Exception SR[CY] < - carry SR[OV] < - overflow l.addi rd,ra,i Add Immediate Signed rd[31:0] < - ra[31:0] + exts(immediate) Range Exception SR[CY] < - carry SR[OV] < - overflow l.addic rd,ra,i Add Immediate Signed and Carry rd[31:0] < - ra[31:0] + exts(immediate) + SR[CY] Range Exception SR[CY] < - carry SR[OV] < - overflow l.div rd,ra,rb Divide Signed rd[31:0] < - ra[31:0] / rb[31:0] Range Exception SR[OV] < - overflow SR[CY] < - carry l.divu rd,ra,rb Divide Unsigned rd[31:0] < - ra[31:0] / rb[31:0] Range Exception SR[OV] < - overflow SR[CY] < - carry l.mac ra,rb Multiply Signed and Accumulate temp[31:0] < - ra[31:0] * rb[31:0] None

Hardware/Software co-design para aplicações de processamento de voz 154 MACHI[31:0]MACLO[31:0] < - temp[31:0] + MACHI[31:0]MACLO[31:0] l.maci rb,i Multiply Immediate Signed and Accumulate temp[31:0] < - ra[31:0] * exts(immediate) MACHI[31:0]MACLO[31:0] < - temp[31:0] + MACHI[31:0]MACLO[31:0] l.msb ra,rb Multiply Signed and Subtract temp[31:0] < - ra[31:0] * rb[31:0] MACHI[31:0]MACLO[31:0] < - MACHI[31:0]MACLO[31:0] - temp[31:0] l.mul rd,ra,rb Multiply Signed rd[31:0] < - ra[31:0] * rb[31:0] SR[OV] < - overflow SR[CY] < - carry l.muli rd,ra,i Multiply Immediate Signed rd[31:0] < - ra[31:0] * Immediate SR[OV] < - overflow SR[CY] < - carry l.mulu rd,ra,rb Multiply Unsigned rd[31:0] < - ra[31:0] * rb[31:0] SR[OV] < - overflow SR[CY] < - carry l.sub rd,ra,rb Subtract Signed rd[31:0] < - ra[31:0] - rb[31:0] SR[CY] < - carry SR[OV] < - overflow None None Range Exception Range Exception Range Exception Range Exception

Hardware/Software co-design para aplicações de processamento de voz 155 Logical Instructions l.and rd,ra,rb And rd[31:0] < - ra[31:0] AND rb[31:0] None l.andi rd,ra,k And with Immediate Half Word rd[31:0] < - ra[31:0] AND extz(immediate) None l.or rd,ra,rb Or rd[31:0] < - ra[31:0] OR rb[31:0] None l.ori Or with Immediate Half Word rd[31:0] < - ra[31:0] OR extz(immediate) None l.ror rd,ra,rb Rotate Right rd[31-rb[4:0]:0] < - ra[31:rb] None rd[31:32-rb[4:0]] < - ra[rb[4:0]-1:0] l.rori rd,ra,l Rotate Right with Immediate rd[31-l:0] < - ra[31:l] None rd[31:32-l] < - ra[l-1:0] l.sll rd,ra,rb Shift Left Logical rd[31:rb[4:0]] < - ra[31-rb[4:0]:0] None rd[rb[4:0]-1:0] < - 0 l.slli rd,ra,l Shift Left Logical with Immediate rd[31:l] < - ra[31-l:0] None rd[l-1:0] < - 0 l.sra rd,ra,rb Shift Right Arithmetic rd[31-rb[4:0]:0] < - ra[31:rb[4:0]] None rd[31:32-rb[4:0]] < - ra[31] l.srai rd,ra,l Shift Right Arithmetic with Immediate rd[31-l:0] < - ra[31:l] None rd[31:32-l] < - ra[31] l.srl rd,ra,rb Shift Right Logical rd[31-rb[4:0]:0] < - ra[31:rb[4:0]] None rd[31:32-rb[4:0]] < - 0 l.srli rd,ra,l Shift Right Logical with Immediate rd[31-l:0] < - ra[31:l] None rd[31:32-l] < - 0 l.xor rd,ra,rb Exclusive Or rd[31:0] < - ra[31:0] XOR rb[31:0] None l.xori rd,ra,i Exclusive Or with Immediate Half Word rd[31:0] < - ra[31:0] XOR exts(immediate) None

Hardware/Software co-design para aplicações de processamento de voz 156 Branch Instructions l.bf N Branch if Flag EA < - exts(immediate < < 2) + BranchInsnAddr None PC < - EA if SR[F] set l.bnf N Branch if No Flag EA < - exts(immediate < < 2) + BranchInsnAddr None PC < - EA if SR[F] cleared l.j N Jump PC < - exts(immediate < < 2) + JumpInsnAddr None l.jal N Jump and Link PC < - exts(immediate < < 2) + JumpInsnAddr None LR < - DelayInsnAddr + 4 l.jalr rb Jump and Link Register PC < - rb None LR < - DelayInsnAddr + 4 l.jr rb Jump Register PC < - rb None l.rfe Return From Exception PC < - EPCR SR < - ESR None

Hardware/Software co-design para aplicações de processamento de voz 157 Register Instructions l.cmov rd,ra,rb Conditional Move rd[31:0] < - SR[F]? ra[31:0] : rb[31:0] None l.extbs rd,ra Extend Byte with Sign rd[31:8] < - ra[7] None rd[7:0] < - ra[7:0] l.extbz rd,ra Extend Byte with Zero rd[31:8] < - 0 None rd[7:0] < - ra[7:0] l.exths rd,ra Extend Half Word with Sign rd[31:16] < - ra[15] None rd[15:0] < - ra[15:0] l.exthz rd,ra Extend Half Word with Zero rd[31:16] < - 0 None rd[15:0] < - ra[15:0] l.extws rd,ra Extend Word with Sign rd[31:0] < - ra[31:0] None l.extwz rd,ra Extend Word with Zero rd[31:0] < - ra[31:0] None l.ff1 rd,ra,rb Find First 1 rd[31:0] < - ra[31]? 32 : ra[30]? 31... ra[0]? 1 : 0 None l.macrc rd MAC Read and Clear synchronize-mac None rd[31:0] < - MACLO[31:0] MACLO[31:0], MACHI[31:0] <- 0 l.mfspr rd,ra,k Move From Special-Purpose Register rd[31:0] < - spr(ra OR Immediate) None l.movhi rd,k Move Immediate High rd[31:0] < - extz(immediate) < < 16 None l.mtspr ra,rb,k Move To Special-Purpose Register spr(ra OR Immediate) < - rb[31:0] None l.sfeq ra,rb Set Flag if Equal SR[F] < - ra[31:0] == rb[31:0] None l.sfeqi ra,i Set Flag if Equal Immediate SR[F] < - ra[31:0] == exts(immediate) None l.sfges ra,rb Set Flag if Greater or Equal Than Signed SR[F] < - ra[31:0] >= rb[31:0] None l.sfgesi ra,i Set Flag if Greater or Equal Than Immediate Signed SR[F] < - ra[31:0] >= exts(immediate) None l.sfgeu ra,rb Set Flag if Greater or Equal ThanUnsigned SR[F] < - ra[31:0] >= rb[31:0] None l.sfgeui ra,i Set Flag if Greater or Equal ThanImmediate SR[F] < - ra[31:0] >= extz(immediate) None Unsigned l.sfgts ra,rb Set Flag if Greater Than Signed SR[F] < - ra[31:0] > rb[31:0] None l.sfgtsi ra,i Set Flag if Greater Than Immediate SR[F] < - ra[31:0] > exts(immediate) None Signed l.sfgtu ra,rb Set Flag if Greater Than Unsigned SR[F] < - ra[31:0] > rb[31:0] None l.sfgtui ra,i Set Flag if Greater Than Immediate SR[F] < - ra[31:0] > extz(immediate) None Unsigned l.sfles ra,rb Set Flag if Less or Equal Than Signed SR[F] < - ra[31:0] < = rb[31:0] None

Hardware/Software co-design para aplicações de processamento de voz 158 l.sflesi ra,i Set Flag if Less or Equal Than Immediate SR[F] < - ra[31:0] < = exts(immediate) None Signed l.sfleu ra,rb Set Flag if Less or Equal Than Unsigned SR[F] < - ra[31:0] < = rb[31:0] None l.sfleui ra,i Set Flag if Less or Equal Than ImmediateUnsigned SR[F] < - ra[31:0] < = extz(immediate) None l.sflts ra,rb Set Flag if Less Than Signed SR[F] < - ra[31:0] < rb[31:0] None l.sfltsi ra,i Set Flag if Less Than Immediate Signed SR[F] < - ra[31:0] < exts(immediate) None l.sfltu ra,rb Set Flag if Less Than Unsigned SR[F] < - ra[31:0] < rb[31:0] None l.sfltui ra,i Set Flag if Less Than Immediate Unsigned SR[F] < - ra[31:0] < extz(immediate) None l.sfne ra,rb Set Flag if Not Equal SR[F] < - ra[31:0]!= rb[31:0] None l.sfnei ra,i Set Flag if Not Equal Immediate SR[F] < - ra[31:0]!= exts(immediate) None Load/Store Instructions l.lbs rd,i(ra) Load Byte and Extend with Sign EA < - exts(immediate) + ra[31:0] rd[7:0] < - (EA)[7:0] rd[31:8] < - (EA)[7] l.lbz rd,i(ra) Load Byte and Extend with Zero EA < - exts(immediate) + ra[31:0] rd[7:0] < - (EA)[7:0] l.lhs rd,i(ra) l.lhz rd,i(ra) l.lws rd,i(ra) l.lwz rd,i(ra) Load Half Word and Extend with Sign Load Half Word and Extend with Zero Load Single Word and Extend wit h Sign Load Single Word and Extend with Zero rd[31:8] < - 0 EA < - exts(immediate) + ra[31:0] rd[15:0] < - (EA)[15:0] rd[31:16] < - (EA)[15] EA < - exts(immediate) + ra[31:0] rd[15:0] < - (EA)[15:0] rd[31:16] < - 0 EA < - exts(immediate) + ra[31:0] rd[31:0] < - (EA)[31:0] EA < - exts(immediate) + ra[31:0] rd[31:0] < - (EA)[31:0] TLB miss Page fault Bus error TLB miss Page fault Bus error TLB miss Page fault Bus error Alignment TLB miss Page fault Bus error Alignment TLB miss Page fault Bus error Alignment TLB miss Page fault Bus error

Hardware/Software co-design para aplicações de processamento de voz 159 Alignment l.sb I(rA),rB Store Byte EA < - exts(immediate) + ra[31:0] (EA)[7:0] < - rb[7:0] l.sh I(rA),rB Store Half Word EA < - exts(immediate) + ra[31:0] (EA)[15:0] < - rb[15:0] l.sw I(rA),rB Store Single Word EA < - exts(immediate) + ra[31:0] (EA)[31:0] < - rb[31:0] TLB miss Page fault Bus error TLB miss Page fault Bus error Alignment TLB miss Page fault Bus error Alignment Others Instructions l.csync Context Syncronization context-synchronization None l.msync Memory Syncronization memory-synchronization None l.nop K No Operation - None l.psync Pipeline Syncronization pipeline-synchronization None l.sys K System Call system-call-exception(k) System Call l.trap K Trap if SR[K] = 1 then trap-exception() Trap exception

Hardware/Software co-design para aplicações de processamento de voz 160 Anexo 6: Funções básicas da norma do codec implementadas em asssembly #define mult_or32(var_out, var1, var2){\ Word32 aux1;\ asm volatile ( "l.mul\t%0,%1,%2 "l.movhi\t%3,0x4000 "l.sfeq\t%3,%0 "l.bnf\t3 "l.srai\t%0,%0,0x000f "l.addi\t%0,r0,0x7fff : "=r" (var_out)\ : "r" (var1), "r" (var2), "r" (aux1) );\ #define mult_i_or32(var_out, var1, var2){\ Word32 aux1;\ asm volatile ( "l.muli\t%0,%1,%2 "l.movhi\t%3,0x4000 "l.sfeq\t%3,%0 "l.bnf\t3 "l.srai\t%0,%0,0x000f "l.addi\t%0,r0,0x7fff : "=r" (var_out)\ : "r" (var1), "i" (var2), "r" (aux1) );\ #define L_mult_OR32(var_out, var1, var2){\ Word32 _aux1;\ asm volatile ( "l.mul\t%0,%1,%2 "l.movhi\t%3,0x4000 "l.sfeq\t%3,%0 "l.bnf\t3 "l.slli\t%0,%0,0x1 "l.addi\t%0,%0,0xffff : "=r" (var_out)\ : "r" (var1), "r" (var2), "r" (_aux1) );\ #define L_mult_i_OR32(var_out, var1, var2){\ Word32 _aux1;\ asm volatile ( "l.muli\t%0,%1,%2 "l.movhi\t%3,0x4000 "l.sfeq\t%3,%0 "l.bnf\t3 "l.slli\t%0,%0,0x1 "l.addi\t%0,%0,0xffff : "=r" (var_out)\ : "r" (var1), "i" (var2), "r" (_aux1) );\ #define L_negate_OR32(L_var_out, L_var1){\ Word32 aux1;\ asm volatile ( "l.movhi\t%2,0x8000 "l.sfeq\t%1,%2 "l.bnf\t4 "l.sub\t%0,r0,%1 "l.movhi\t%0,0x7fff "l.ori\t%0,%0,0xffff : "=r" (L_var_out) \ : "r" (L_var1), "r" (aux1) );\ #define negate_or32(var_out, var1){\ asm volatile ( "l.sfeqi\t%1,0x8000 "l.bnf\t3 "l.sub\t%0,r0,%1 "l.ori\t%0,%0,0x7fff : "=r" (var_out) \ : "r" (var1) ); \ #define abs_s_or32(var_out, var1){\ asm volatile ( "l.sfgesi\t%1,0x0000 "l.bf\t6 "l.extws\t%0,%1

Hardware/Software co-design para aplicações de processamento de voz 161 "l.sfeqi\t%1,0x8000 "l.bnf\t3 "l.sub\t%0,r0,%1 "l.addi\t%0,r0,0x7fff : "=r" (var_out) \ : "r" (var1) ); \ #define L_abs_OR32(L_var_out, L_var1){\ Word32 aux1;\ asm volatile ( "l.sfgesi\t%1,0x0000 "l.bf\t7 "l.extws\t%0,%1 "l.movhi\t%2,0x8000 "l.sfeq\t%1,%2 "l.bnf\t3 "l.sub\t%0,r0,%1 "l.addi\t%0,%0,0xffff : "=r" (L_var_out) \ : "r" (L_var1), "r" (aux1) );\ /* --> Instruções MAC <-- */ #define L_mac_init_0_OR32(){\ Word32 aux1;\ asm volatile ( "l.macrc\t%0 : : "r" ( aux1) );\ #define L_mac_OR32(var1, var2){\ asm volatile ( "l.mac\t%0,%1 : : "r" (var1), "r" (var2) );\ #define L_mac_i_OR32(var1, var2){\ asm volatile ( "l.maci\t%0,%1"\ : : "r" (var1), "i" (var2) );\ #define L_msu_OR32(var1, var2){\ asm volatile ( "l.msb\t%0,%1"\ : : "r" (var1), "r" (var2) );\ #define L_mac_out_pos_OR32(L_var_out){\ Word32 aux1=0;\ asm volatile ( "l.mfspr\t%3,r0,%1 "l.sfgtsi\t%3,0x0 "l.bf\t10 "l.mfspr\t%0,r0,%2 "l.movhi\t%3,0x4000 "l.sfges\t%0,%3 "l.bf\t6 "l.sfltsi\t%0,0x0 "l.bf\t4 "l.slli\t%0,%0,0x1 "l.j\t4 "l.nop\t0x0 "l.movhi\t%0,0x7fff "l.ori\t%0,%0,0xffff : "=r" (L_var_out) \ : "K" (SPR_MACHI), "K" (SPR_MACLO), "r" ( aux1) );\ #define L_mac_out_add_pos_OR32(L_var1, L_var_out){\ Word32 aux1=0;\ Word32 aux2=8;\ asm volatile ( "l.mfspr\t%3,r0,%1 "l.sfgtsi\t%3,0x0 "l.bf\t19 "l.mfspr\t%0,r0,%2 "l.movhi\t%3,0x4000 "l.sfges\t%0,%3 "l.bf\t15 "l.slli\t%0,%0,0x1 \ "l.xor\t%3,%0,%4 "l.sfltsi\t%3,0x0 "l.bf\t13 "l.add\t%0,%0,%4

Hardware/Software co-design para aplicações de processamento de voz 162 "l.xor\t%3,%4,%0 "l.sfgesi\t%3,0x0 "l.bf\t9 "l.sfgesi\t%4,0x0 "l.movhi\t%3,0x7fff "l.ori\t%3,%3,0xffff "l.movhi\t%5,0x8000 \ "l.j\t4 "l.cmov\t%0,%3,%5 "l.movhi\t%0,0x7fff "l.ori\t%0,%0,0xffff : "=r" (L_var_out) \ : "K" (SPR_MACHI), "K" (SPR_MACLO), "r" ( aux1), "r" (L_var1), "r" ( aux2));\ #define L_mac_out_OR32(L_var_out){\ Word32 aux1=0;\ asm volatile ( "l.mfspr\t%3,r0,%1 "l.sfgtsi\t%3,0x0 "l.bf\t13 "l.sfltsi\t%3,0xffff "l.bf\t14 "l.mfspr\t%0,r0,%2 "l.movhi\t%3,0x4000 "l.sfges\t%0,%3 "l.bf\t7 "l.movhi\t%3,0xc000 "l.sfles\t%0,%3 "l.bf\t7 "l.slli\t%0,%0,0x1 "l.j\t6 "l.nop\t0x0 "l.movhi\t%0,0x7fff "l.j\t3 "l.ori\t%0,%0,0xffff "l.movhi\t%0,0x8000 : "=r" (L_var_out) \ : "K" (SPR_MACHI), "K" (SPR_MACLO), "r" ( aux1) );\ #define L_mac_out_add_OR32(L_var1, L_var_out){\ Word32 aux1=0;\ Word32 aux2=8;\ asm volatile ( "l.mfspr\t%3,r0,%1 "l.sfgtsi\t%3,0x0 "l.bf\t13 "l.sfltsi\t%3,0xffff "l.bf\t14 "l.mfspr\t%0,r0,%2 "l.movhi\t%3,0x4000 "l.sfges\t%0,%3 "l.bf\t18 "l.movhi\t%3,0xc000 "l.sfles\t%0,%3 "l.bf\t18 "l.slli\t%0,%0,0x1 \ "l.xor\t%3,%0,%4 "l.sfltsi\t%3,0x0 "l.bf\t15 "l.add\t%0,%0,%4 "l.xor\t%3,%4,%0 "l.sfgesi\t%3,0x0 "l.bf\t11 "l.sfgesi\t%4,0x0 "l.movhi\t%3,0x7fff "l.ori\t%3,%3,0xffff "l.movhi\t%5,0x8000 \ "l.j\t6 "l.cmov\t%0,%3,%5 "l.movhi\t%0,0x7fff "l.j\t3 "l.ori\t%0,%0,0xffff "l.movhi\t%0,0x8000 : "=r" (L_var_out) \

Hardware/Software co-design para aplicações de processamento de voz 163 "r" ( aux2));\ : "K" (SPR_MACHI), "K" (SPR_MACLO), "r" ( aux1), "r" (L_var1), #define L_mac_out_round_OR32(var_out){\ Word32 aux1=0;\ asm volatile ( "l.mfspr\t%3,r0,%1 "l.sfgtsi\t%3,0x0 "l.bf\t16 "l.sfltsi\t%3,0xffff "l.bf\t17 "l.mfspr\t%0,r0,%2 "l.movhi\t%3,0x3fff "l.ori\t%3,%3,0xc000 "l.sfges\t%0,%3 "l.bf\t9 "l.movhi\t%3,0xc000 "l.sfles\t%0,%3 "l.bf\t9 "l.slli\t%0,%0,0x1 "l.ori\t%3,r0,0x8000 "l.add\t%0,%0,%3 "l.j\t6 "l.srai\t%0,%0,0x10 "l.addi\t%0,r0,0x7fff "l.j\t3 "l.nop 0x0 "l.addi\t%0,r0,0x8000 : "=r" (var_out) \ : "K" (SPR_MACHI), "K" (SPR_MACLO), "r" ( aux1) );\ #define L_mac_out_round_pos_OR32(var_out){\ Word32 aux1=0;\ asm volatile ( "l.mfspr\t%3,r0,%1 /* --> fim instruções MAC <-- */ "l.sfgtsi\t%3,0x0 "l.bf\t16 "l.sfltsi\t%3,0xffff "l.bf\t17 "l.mfspr\t%0,r0,%2 "l.movhi\t%3,0x3fff "l.ori\t%3,%3,0xc000 "l.sfges\t%0,%3 "l.bf\t9 "l.movhi\t%3,0xc000 "l.sfles\t%0,%3 "l.bf\t9 "l.slli\t%0,%0,0x1 "l.ori\t%3,r0,0x8000 "l.add\t%0,%0,%3 "l.j\t6 "l.srai\t%0,%0,0x10 "l.addi\t%0,r0,0x7fff "l.j\t3 "l.nop 0x0 "l.addi\t%0,r0,0x8000 : "=r" (var_out) \ : "K" (SPR_MACHI), "K" (SPR_MACLO), "r" ( aux1) );\ #define add_or32(var_out, var1, var2){\ volatile Word32 aux1=0;\ volatile Word32 aux2=var1;\ asm volatile ( \ \ "l.xor\t%3,%1,%2 "l.sfltsi\t%3,0x0 "l.bf\t10 "l.add\t%0,%1,%2 "l.exths\t%0,%0 "l.xor\t%3,%4,%0 "l.sfgesi\t%3,0x0 "l.bf\t5 "l.sfgesi\t%1,0x0 "l.addi\t%3,r0,0x7fff "l.addi\t%4,r0,0x8000

Hardware/Software co-design para aplicações de processamento de voz 164 "l.cmov\t%0,%3,%4 : "=r" (var_out) \ : "r" (var1), "r" (var2), "r" ( aux1), "r" ( aux2));\ #define sub_or32(var_out, var1, var2){\ volatile Word32 aux1;\ volatile Word32 aux2=var1;\ asm volatile ( \ \ "l.xor\t%3,%1,%2 "l.sfgesi\t%3,0x0 "l.bf\t10 "l.sub\t%0,%1,%2 "l.exths\t%0,%0 "l.xor\t%3,%4,%0 "l.sfgesi\t%3,0x0 "l.bf\t5 "l.sfgesi\t%1,0x0 "l.addi\t%3,r0,0x7fff "l.addi\t%4,r0,0x8000 "l.cmov\t%0,%3,%4 : "=r" (var_out) \ : "r" (var1), "r" (var2), "r" ( aux1), "r" ( aux2));\ #define add_i_or32(var_out, var1, var2){\ volatile Word32 aux1=0;\ volatile Word32 aux2=var1;\ asm volatile ( \ \ "l.xori\t%3,%1,%2 "l.sfltsi\t%3,0x0 "l.bf\t10 "l.addi\t%0,%1,%2 "l.exths\t%0,%0 "l.xor\t%3,%4,%0 "l.sfgesi\t%3,0x0 "l.bf\t5 "l.sfgesi\t%1,0x0 "l.addi\t%3,r0,0x7fff "l.addi\t%4,r0,0x8000 "l.cmov\t%0,%3,%4 : "=r" (var_out) \ : "r" (var1), "i" (var2), "r" ( aux1), "r" ( aux2));\ #define div_s_or32(var_out, var1, var2){\ Word32 aux1=0;\ Word32 aux2=2;\ asm volatile ( "l.addi\t%0,r0,0x0 "l.ori\t%4,%1,0x0 "l.addi\t%3,r0,0xffff "l.addi\t%3,%3,0x1 "l.sfltsi\t%3,0xf "l.bnf\t8 "l.slli\t%4,%4,0x1 "l.sfges\t%4,%2 "l.bnf\t-5 "l.slli\t%0,%0,0x1 "l.sub\t%4,%4,%2 "l.j\t-8 "l.addi\t%0,%0,0x1 : "=r" (var_out) \ : "r" (var1), "r" (var2), "r" ( aux1), "r" ( aux2));\ #define shr_or32(var_out, var1, var2){\ asm volatile ( "l.sra\t%0,%1,%2 : "=r" (var_out) \ : "r" (var1), "r" (var2) );\ #define shr_i_or32(var_out, var1, var2){\ asm volatile ( "l.srai\t%0,%1,%2 : "=r" (var_out) \

Hardware/Software co-design para aplicações de processamento de voz 165 : "r" (var1), "i" (var2) );\ #define L_shr_OR32(L_var_out, L_var1, var2){\ Word32 aux1 = 0x1f;\ asm volatile ( "l.sfgtsi\t%2,0x1f "l.cmov\t%3,%3,%2 "l.sra\t%0,%1,%3 : "=r" (L_var_out) \ : "r" (L_var1), "r" (var2), "r" ( aux1));\ #define L_shr_i_OR32(L_var_out, L_var1, var2){\ asm volatile ( "l.srai\t%0,%1,%2 : "=r" (L_var_out) \ : "r" (L_var1), "i" (var2) );\ #define L_add_OR32(var_out, var1, var2){\ Word32 aux1=0;\ Word32 aux2=var1;\ asm volatile ( "l.xor\t%3,%1,%2 "l.sfltsi\t%3,0x0 "l.bf\t10 "l.add\t%0,%1,%2 "l.xor\t%3,%4,%0 "l.sfgesi\t%3,0x0 "l.bf\t6 "l.sfgesi\t%1,0x0 "l.movhi\t%3,0x7fff "l.ori\t%3,%3,0xffff "l.movhi\t%4,0x8000 "l.cmov\t%0,%3,%4 : "=r" (var_out) \ : "r" (var1), "r" (var2), "r" ( aux1), "r" ( aux2));\ #define L_add_i_OR32(var_out, var1, var2){\ volatile Word32 aux1=0;\ volatile Word32 aux2=var1;\ asm volatile ( "l.xori\t%3,%1,%2 "l.sfltsi\t%3,0x0 "l.bf\t10 "l.addi\t%0,%1,%2 "l.xor\t%3,%4,%0 "l.sfgesi\t%3,0x0 "l.bf\t6 "l.sfgesi\t%1,0x0 "l.movhi\t%3,0x7fff "l.ori\t%3,%3,0xffff "l.movhi\t%4,0x8000 "l.cmov\t%0,%3,%4 : "=r" (var_out) \ : "r" (var1), "i" (var2), "r" ( aux1), "r" ( aux2));\ #define L_sub_OR32(var_out, var1, var2){\ volatile Word32 aux1=0;\ volatile Word32 aux2=var1;\ asm volatile ( "l.xor\t%3,%1,%2 "l.sfgesi\t%3,0x0 "l.bf\t10 "l.sub\t%0,%1,%2 "l.xor\t%3,%4,%0 "l.sfgesi\t%3,0x0 "l.bf\t6 "l.sfgesi\t%1,0x0 "l.movhi\t%3,0x7fff "l.ori\t%3,%3,0xffff "l.movhi\t%4,0x8000 "l.cmov\t%0,%3,%4 : "=r" (var_out) \ : "r" (var1), "r" (var2), "r" ( aux1), "r" ( aux2));\ #define L_shl_OR32(L_var_out, L_var1, var2){\ Word32 aux1=0x3fffffff;\ Word32 aux2=0xc0000000;\

Hardware/Software co-design para aplicações de processamento de voz 166 ( aux3));\ Word32 aux3;\ asm volatile ( "l.sflesi\t%2,0x0 "l.bf\t20 "l.cmov\t%0,%1,%1 "l.movhi\t%3,0x3fff "l.ori\t%3,%3,0xffff "l.movhi\t%4,0xc000 "l.cmov\t%5,%2,%2 "l.sfges\t%0,%3 "l.bf\t8 "l.sfles\t%0,%4 "l.bf\t6 "l.addi\t%5,%5,0xffff "l.sfgtsi\t%5,0x0 "l.bf\t-6 "l.slli\t%0,%0,0x1 "l.j\t6 "l.sfgesi\t%0,0x0 "l.movhi\t%3,0x7fff "l.ori\t%3,%3,0xffff "l.movhi\t%4,0x8000 "l.cmov\t%0,%3,%4 : "=r" (L_var_out) \ : "r" (L_var1), "r" (var2), "r" ( aux1), "r" ( aux2), "r" #define L_shl_i_OR32(L_var_out, L_var1, var2) L_shl_OR32(L_var_out, L_var1, var2) #define L_shl_round_OR32(var_out, L_var1, var2){\ Word32 aux1=0x3fffffff;\ Word32 aux2=0xc0000000;\ Word32 aux3;\ asm volatile ( "l.sflesi\t%2,0x0 "l.bf\t20 "l.cmov\t%0,%1,%1 "l.movhi\t%3,0x3fff "l.ori\t%3,%3,0xffff "l.movhi\t%4,0xc000 "l.cmov\t%5,%2,%2 "l.sfges\t%0,%3 "l.bf\t8 "l.sfles\t%0,%4 "l.bf\t6 "l.addi\t%5,%5,0xffff "l.sfgtsi\t%5,0x0 "l.bf\t-6 "l.slli\t%0,%0,0x1 "l.j\t6 "l.sfgesi\t%0,0x0 "l.movhi\t%3,0x7fff "l.ori\t%3,%3,0xffff "l.movhi\t%4,0x8000 "l.cmov\t%0,%3,%4 \ "l.srai\t%0,%0,0xf "l.addi\t%0,%0,0x1 "l.srai\t%0,%0,0x1 "l.sfgtsi\t%0,0x7fff "l.bnf\t3 ( aux3));\ "l.nop\t0x0 "l.addi\t%0,r0,0x7fff : "=r" (var_out) \ : "r" (L_var1), "r" (var2), "r" ( aux1), "r" ( aux2), "r" #define L_shl_i_round_OR32(var_out, L_var1, var2) L_shl_round_OR32(var_out, L_var1, var2) #define L_shl_extract_h_OR32(var_out, L_var1, var2){\ Word32 aux1=0x3fffffff;\ Word32 aux2=0xc0000000;\ Word32 aux3;\ asm volatile ( "l.sflesi\t%2,0x0 "l.bf\t20 "l.cmov\t%0,%1,%1 "l.movhi\t%3,0x3fff

Hardware/Software co-design para aplicações de processamento de voz 167 ( aux3));\ "l.ori\t%3,%3,0xffff "l.movhi\t%4,0xc000 "l.cmov\t%5,%2,%2 "l.sfges\t%0,%3 "l.bf\t8 "l.sfles\t%0,%4 "l.bf\t6 "l.addi\t%5,%5,0xffff "l.sfgtsi\t%5,0x0 "l.bf\t-6 "l.slli\t%0,%0,0x1 "l.j\t6 "l.sfgesi\t%0,0x0 "l.movhi\t%3,0x7fff "l.ori\t%3,%3,0xffff "l.movhi\t%4,0x8000 "l.cmov\t%0,%3,%4 \ "l.srai\t%0,%0,0x10 : "=r" (var_out) \ : "r" (L_var1), "r" (var2), "r" ( aux1), "r" ( aux2), "r" #define L_shl_i_extract_h_OR32(var_out, L_var1, var2) L_shl_extract_h_OR32(var_out, L_var1, var2) #define L_shl_n_sat_OR32(var_out, L_var1, var2){\ asm volatile ( "l.sll\t%0,%1,%2 : "=r" (var_out) \ : "r" (L_var1), "r" (var2));\ #define L_shl_n_sat_extract_h_OR32(var_out, L_var1, var2){\ asm volatile ( "l.sll\t%0,%1,%2 "l.srai\t%0,%0,0x10 : "=r" (var_out) \ : "r" (L_var1), "r" (var2));\ #define L_shl_n_sat_round_OR32(var_out, L_var1, var2){\ asm volatile ( "l.sll\t%0,%1,%2 \ "l.srai\t%0,%0,0xf "l.addi\t%0,%0,0x1 "l.srai\t%0,%0,0x1 "l.sfgtsi\t%0,0x7fff "l.bnf\t3 "l.nop\t0x0 "l.addi\t%0,r0,0x7fff : "=r" (var_out) \ : "r" (L_var1), "r" (var2));\ #define shl_or32(var_out, var1, var2){\ Word32 aux1=0xf;\ Word32 aux2;\ asm volatile ( "l.sfgtsi\t%2,0xf "l.cmov\t%3,%3,%2 "l.sll\t%0,%1,%3 "l.sfgtsi\t%0,0x7fff "l.bf\t3 "l.sfltsi\t%0,0x8000 "l.bnf\t5 "l.sfgesi\t%1,0x0 "l.addi\t%3,r0,0x7fff "l.addi\t%4,r0,0x8000 "l.cmov\t%0,%3,%4 : "=r" (var_out) \ : "r" (var1), "r" (var2), "r" ( aux1), "r" ( aux2) );\ #define shl_i_or32(var_out, var1, var2){\ Word32 aux1;\ Word32 aux2=0xffff8000;\ asm volatile ( "l.slli\t%0,%1,%2 "l.sfgtsi\t%0,0x7fff

Hardware/Software co-design para aplicações de processamento de voz 168 "l.bf\t3 "l.sfltsi\t%0,0x8000 "l.bnf\t5 "l.sfgesi\t%1,0x0 "l.addi\t%3,r0,0x7fff "l.addi\t%4,r0,0x8000 "l.cmov\t%0,%3,%4 : "=r" (var_out) \ : "r" (var1), "i" (var2), "r" ( aux1), "r" ( aux2) );\ #define shl_n_sat_or32(var_out, var1, var2){\ asm volatile ( "l.sll\t%0,%1,%2 : "=r" (var_out) \ : "r" (var1), "r" (var2) );\ #define shl_i_n_sat_or32(var_out, var1, var2){\ asm volatile ( "l.slli\t%0,%1,%2 : "=r" (var_out) \ : "r" (var1), "i" (var2) );\ #define norm_s_or32(var_out, var1){\ Word32 aux1;\ asm volatile ( "l.sfeqi\t%1,0x0 "l.bf\t14 "l.addi\t%0,r0,0x0 "l.sfeqi\t%1,0xffff "l.bf\t11 "l.addi\t%0,r0,0xf "l.cmov\t%2,%1,%1 "l.sfltsi\t%1,0x0 "l.bnf\t3 "l.addi\t%0,r0,0xffff "l.xori\t%2,%1,0xffff "l.addi\t%0,%0,0x1 "l.sfltsi\t%2,0x4000 "l.bf\t-2 "l.slli\t%2,%2,0x1 : "=r" (var_out) \ : "r" (var1), "r" ( aux1) );\ #define norm_l_or32(var_out, var1){\ Word32 aux1;\ Word32 aux2=0x40000000;\ asm volatile ( "l.movhi\t%3,0x4000 "l.sfeqi\t%1,0x0 "l.bf\t14 "l.addi\t%0,r0,0x0 "l.sfeqi\t%1,0xffff "l.bf\t11 "l.addi\t%0,r0,0x1f "l.cmov\t%2,%1,%1 "l.sfltsi\t%1,0x0 "l.bnf\t3 "l.addi\t%0,r0,0xffff "l.xori\t%2,%1,0xffff "l.addi\t%0,%0,0x1 "l.sflts\t%2,%3 "l.bf\t-2 "l.slli\t%2,%2,0x1 : "=r" (var_out) \ : "r" (var1), "r" ( aux1), "r" ( aux2) );\

Hardware/Software co-design para aplicações de processamento de voz 169 Anexo 7: tt_ssram.v ///////////////////////////////////////////////////////////////////// //// //// //// OpenCores Memory Controller Testbench //// //// SSRAM memory devices tests //// //// This file is being included by the main testbench //// //// //// //// Author: Richard Herveille //// //// richard@asics.ws //// //// //// //// //// //// Downloaded from: http://www.opencores.org/cores/mem_ctrl/ //// //// //// ///////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2001, 2002 Richard Herveille //// //// richard@asics.ws //// //// //// //// This source file may be used and distributed without //// //// restriction provided that this copyright statement is not //// //// removed from the file and that any derivative work contains //// //// the original copyright notice and the associated disclaimer.//// //// //// //// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// //// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// //// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// //// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// //// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// //// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// //// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// //// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// //// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// //// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// //// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// //// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// //// POSSIBILITY OF SUCH DAMAGE. //// //// //// ///////////////////////////////////////////////////////////////////// // CVS Log // // $Id: tst_ssram.v,v 1.1 2002/03/06 15:10:34 rherveille Exp $ // // $Date: 2002/03/06 15:10:34 $ // $Revision: 1.1 $ // $Author: rherveille $ // $Locker: $ // $State: Exp $ // // Change History: // $Log: tst_ssram.v,v $ // Revision 1.1 2002/03/06 15:10:34 rherveille // Initial release // // //////////////////////////////// // SSRAM Sequential access test // // 1) Tests ssram sequential address access // 2) Tests page switch // 3) Test burst-action by filling memory backwards (high addresses first) task tst_ssram_seq; parameter MAX_CYC_DELAY = 1;//5; parameter MAX_STB_DELAY =1;//5; parameter SSRAM_TST_RUN = 8;//128; parameter [31:0] SSRAM_TST_STARTA = `SSRAM_LOC + (SSRAM_TST_RUN<<2); parameter [ 7:0] SSRAM_SEL = SSRAM_TST_STARTA[28:21]; integer n, k; reg [31:0] my_adr, dest_adr;

Hardware/Software co-design para aplicações de processamento de voz 170 reg [31:0] my_dat; reg [15:0] tmp0, tmp1; // SSRAM Mode Register bits reg [31:0] csc_data, tms_data; integer cyc_delay, stb_delay, bl; begin $display("\n\n --- SSRAM SEQUENTIAL ACCESS TEST ---\n\n"); // clear Wishbone-Master-model current-error-counter wbm.set_cur_err_cnt(0); hardwire???) csc_data = { 8'h00, SSRAM_SEL, 4'h0, 1'b0, 1'b0, 1'b0, 1'b0, 2'b00, 2'b10, 3'b001, 1'b1 ; // reserved // SEL // reserved // parity enabled // KRO, no meaning for ssram // BAS, no meaning for ssram // WP // MS, no meaning for ssram // BW == 32bit bus. Always for ssram (maybe // MEM_TYPE == SDRAM // EN == chip select enabled // tms_data is unused for ssrams tms_data = { 32'hx ; register (CSC4) register (TMS4) // program chip select registers $display("\nprogramming SSRAM chip select register."); wbm.wb_write(0, 0, 32'h6000_0030, csc_data); // program cs4 config $display("programming SSRAM timing register."); wbm.wb_write(0, 0, 32'h6000_0034, tms_data); // program cs4 timing // check written data wbm.wb_cmp(0, 0, 32'h6000_0030, csc_data); wbm.wb_cmp(0, 0, 32'h6000_0034, tms_data); +1) +1) +1) cyc_delay = 0; stb_delay = 0; for (cyc_delay = 0; cyc_delay <= MAX_CYC_DELAY; cyc_delay = cyc_delay for (stb_delay = 0; stb_delay <= MAX_STB_DELAY; stb_delay = stb_delay for (bl = 1; bl <= 8 ; bl = bl begin $display("\nssram sequential test. BL = %d, CYC-delay = %d, STB-delay = ", bl, cyc_delay, stb_delay); -bl) <<2); but with linear bursts // fill sdrams $display("filling SSRAM memory..."); my_dat = 0; for (n=0; n < SSRAM_TST_RUN; n=n+1) begin my_adr = SSRAM_TST_STARTA + ( (SSRAM_TST_RUN -n for (k=0; k < bl; k=k+1) begin // fill destination backwards, dest_adr = my_adr + (k<<2); + cyc_delay + stb_delay; tmp0 = ~dest_adr[15:0] + bl

Hardware/Software co-design para aplicações de processamento de voz 171 + cyc_delay + stb_delay; tmp1 my_dat = dest_adr[15:0] + bl = {tmp0, tmp1; stb_delay, dest_adr, my_dat); de leitura e de teste no master 1 \n",$time); stb_delay, dest_adr, my_dat ) ; end end wbm.wb_write(cyc_delay, $display (" time = %f operação wbm.wb_cmp( cyc_delay, // read sdrams /* $display("verifying SSRAM memory contents..."); my_dat = 0; for (n=0; n < SSRAM_TST_RUN; n=n+1) begin my_adr = n<<2; dest_adr = SSRAM_TST_STARTA + my_adr; cyc_delay + stb_delay; cyc_delay + stb_delay; tmp0 = ~dest_adr[15:0] + bl + tmp1 = dest_adr[15:0] + bl + my_dat = {tmp0, tmp1; dest_adr, my_dat); end end*/ wbm.wb_cmp(cyc_delay, stb_delay, end endtask // test_ssram_seq repeat(10) @(posedge wb_clk); //wait a while // show Wishbone-Master-model current-error-counter wbm.show_cur_err_cnt;

Hardware/Software co-design para aplicações de processamento de voz 172 Anexo 8: bench.v ///////////////////////////////////////////////////////////////////// //// //// //// OpenCores Memory Controller Testbench //// //// Main testbench //// //// //// //// Author: Richard Herveille //// //// richard@asics.ws //// //// //// //// //// //// Downloaded from: http://www.opencores.org/cores/mem_ctrl/ //// //// //// ///////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2001 Richard Herveille //// //// richard@asics.ws //// //// //// //// This source file may be used and distributed without //// //// restriction provided that this copyright statement is not //// //// removed from the file and that any derivative work contains //// //// the original copyright notice and the associated disclaimer.//// //// //// //// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// //// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// //// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// //// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// //// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// //// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// //// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// //// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// //// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// //// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// //// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// //// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// //// POSSIBILITY OF SUCH DAMAGE. //// //// //// ///////////////////////////////////////////////////////////////////// // ToDo: // 1) add power-on configuration // 2) test SSRAM // 3) test synchronous devices??? // // CVS Log // // $Id: bench.v,v 1.1 2002/03/06 15:10:34 rherveille Exp $ // // $Date: 2002/03/06 15:10:34 $ // $Revision: 1.1 $ // $Author: rherveille $ // $Locker: $ // $State: Exp $ // // Change History: // $Log: bench.v,v $ // Revision 1.1 2002/03/06 15:10:34 rherveille // Initial release // // `include "timescale.v" `define SDRAM_ROWA_HI 12 // row address hi-bit `define SDRAM_COLA_HI 8 // column address hi-bit `define BA_MASK 32'h0000_00e0 // base address mask `define SDRAM1_LOC 32'h0400_0000 // location of sdram1 in address-space `define SDRAM2_LOC 32'h0800_0000 // location of sdram2 in address-space `define SRAM_LOC 32'h0C00_0000 // location of srams in address-space `define SSRAM_LOC 32'h1000_0000 // location of ssrams in address-space module bench_top();

Hardware/Software co-design para aplicações de processamento de voz 173 // // internal wires // reg wb_clk; reg mc_clk; reg wb_rst; wire [31:0] wb_dat_i, wb_dat_o; wire [31:0] wb_adr_o; wire wb_cyc_o, wb_stb_o; wire [ 3:0] wb_sel_o; wire wb_ack_i, wb_err_i, wb_rty_i; wire wb_mc_stb; wire [23:0] mc_adr_o; wire [31:0] mc_dq, mc_dq_o; wire [ 3:0] mc_dp, mc_dp_o, pbus_o, pbus_i; reg [ 3:0] set_par; wire [31:0] par_con; reg sel_par, sel_pbus; wire par_sdram_cs; wire mc_doe_o; wire [ 3:0] mc_dqm_o; wire mc_we_o, mc_oe_o; wire mc_ras_o, mc_cas_o, mc_cke_o; wire [ 7:0] mc_cs_o; wire mc_pad_oe; wire mc_adsc_o, mc_adv_o, mc_zz_o; // ssram connections wire ext_br, ext_bg; // // hookup modules // // hookup watch-dog counter watch_dog #(1024) wdog (.clk(wb_clk),.cyc_i(wbm1_cyc_o),.ack_i(wbm1_ack_i),.adr_i(wb_adr_i) ); // hookup external bus-master model bm_model ext_bm(.br(ext_br),.bg(ext_bg),.chk(mc_pad_oe) ); // hookup ERR checker err_check err_chk(wb_err_i, sel_par); // hookup CSn checker cs_check cs_chec(mc_cs_o); // hookup memory controller mc_top dut ( // wishbone interface.clk_i(wb_clk),.rst_i(wb_rst),.wb_data_i(wb_dat_o),.wb_data_o(wb_dat_i),.wb_addr_i(wb_adr_o),.wb_sel_i(wb_sel_o),.wb_we_i(wb_we_o),.wb_cyc_i(wb_cyc_o),.wb_stb_i(wb_stb_o),.wb_ack_o(wb_ack_i),.wb_err_o(wb_err_i), // memory controller.susp_req_i(1'b0),.resume_req_i(1'b0),.suspended_o(),.poc_o(),

Hardware/Software co-design para aplicações de processamento de voz 174 );.mc_clk_i(mc_clk),.mc_br_pad_i(ext_br),.mc_bg_pad_o(ext_bg),.mc_ack_pad_i(1'b0),.mc_addr_pad_o(mc_adr_o),.mc_data_pad_i(mc_dq),.mc_data_pad_o(mc_dq_o),.mc_dp_pad_i(pbus_i), // attach parity bus.mc_dp_pad_o(mc_dp_o),.mc_doe_pad_doe_o(mc_doe_o),.mc_dqm_pad_o(mc_dqm_o),.mc_oe_pad_o_(mc_oe_o),.mc_we_pad_o_(mc_we_o),.mc_cas_pad_o_(mc_cas_o),.mc_ras_pad_o_(mc_ras_o),.mc_cke_pad_o_(mc_cke_o),.mc_cs_pad_o_(mc_cs_o),.mc_sts_pad_i(1'b0),.mc_rp_pad_o_(),.mc_vpen_pad_o(),.mc_adsc_pad_o_(mc_adsc_o),.mc_adv_pad_o_(mc_adv_o),.mc_zz_pad_o(mc_zz_o),.mc_coe_pad_coe_o(mc_pad_oe) // assign memory controller stb_signal assign wb_mc_stb = wb_adr_o[31]; // generate output buffers for memory controller assign mc_dq = mc_doe_o? mc_dq_o : 32'bz; assign mc_dp = mc_doe_o? mc_dp_o : 4'bz; // hookup ssrams (CHIP SELECT 4) mt58l1my18d ssram0 (.Dq( {par_con[24], par_con[16], mc_dq[31:16] ),.Addr(mc_adr_o[19:0]), );.Mode(1'b0), // This input (sometimes called LBO) selects burst order // 1'b0 = linear burst, 1'b1 = interleaved burst.adv_n(mc_adv_o),.clk(mc_clk),.adsc_n(mc_adsc_o),.adsp_n(1'b1),.bwa_n(mc_dqm_o[3]),.bwb_n(mc_dqm_o[2]), // or the otherway around.bwe_n(mc_we_o),.gw_n(1'b1), //??.Ce_n(mc_cs_o[4]),.Ce2(1'b1),.Ce2_n(1'b0),.Oe_n(mc_oe_o),.Zz(mc_zz_o) mt58l1my18d ssram1 (.Dq( {par_con[8], par_con[0], mc_dq[15:0] ),.Addr(mc_adr_o[19:0]), );.Mode(1'b0), // This input (sometimes called LBO) selects burst order // 1'b0 = linear burst, 1'b1 = interleaved burst.adv_n(mc_adv_o),.clk(mc_clk),.adsc_n(mc_adsc_o),.adsp_n(1'b1),.bwa_n(mc_dqm_o[1]),.bwb_n(mc_dqm_o[0]), // or the otherway around.bwe_n(mc_we_o),.gw_n(1'b1),.ce_n(mc_cs_o[4]),.ce2(1'b1),.ce2_n(1'b0),.oe_n(mc_oe_o),.zz(mc_zz_o) // hookup sdrams (CHIP SELECT 3) mt48lc16m16a2 sdram0_3(

Hardware/Software co-design para aplicações de processamento de voz 175 );.Dq(mc_dq[31:16]),.Addr(mc_adr_o[12:0]),.Ba(mc_adr_o[14:13]),.Clk(mc_clk),.Cke(mc_cke_o),.Cs_n(mc_cs_o[3]),.Ras_n(mc_ras_o),.Cas_n(mc_cas_o),.We_n(mc_we_o),.Dqm(mc_dqm_o[3:2]) mt48lc16m16a2 sdram1_3(.dq(mc_dq[15:0]),.addr(mc_adr_o[12:0]),.ba(mc_adr_o[14:13]),.clk(mc_clk),.cke(mc_cke_o),.cs_n(mc_cs_o[3]),.ras_n(mc_ras_o),.cas_n(mc_cas_o),.we_n(mc_we_o),.dqm(mc_dqm_o[1:0]) ); // hookup sdrams (CHIP SELECT 2 or PARITY) assign pbus_o = sel_pbus? (sel_par? mc_dp : set_par) : mc_dq; assign par_con = {7'bz, pbus_o[3], 7'bz, pbus_o[2], 7'bz, pbus_o[1], 7'bz, pbus_o[0]; assign pbus_i = {par_con[24], par_con[16], par_con[8], par_con[0]; assign par_sdram_cs = sel_pbus? mc_cs_o[3] : mc_cs_o[2]; mt48lc16m16a2 sdram0_2(.dq(par_con[31:16]),.addr(mc_adr_o[12:0]),.ba(mc_adr_o[14:13]),.clk(mc_clk),.cke(mc_cke_o),.cs_n(par_sdram_cs),.ras_n(mc_ras_o),.cas_n(mc_cas_o),.we_n(mc_we_o),.dqm(mc_dqm_o[3:2]) ); mt48lc16m16a2 sdram1_2(.dq(par_con[15:0]),.addr(mc_adr_o[12:0]),.ba(mc_adr_o[14:13]),.clk(mc_clk),.cke(mc_cke_o),.cs_n(par_sdram_cs),.ras_n(mc_ras_o),.cas_n(mc_cas_o),.we_n(mc_we_o),.dqm(mc_dqm_o[1:0]) ); // hookup asynchronous srams (CHIP SELECT 1) A8Kx8 asram0 (.Address(mc_adr_o[12:0]),.dataIO(mc_dq[31:24]),.OEn(mc_oe_o),.CE1n(mc_cs_o[1]),.CE2(1'b1),.WEn(mc_we_o) ); A8Kx8 asram1 (.Address(mc_adr_o[12:0]),.dataIO(mc_dq[23:16]),.OEn(mc_oe_o),.CE1n(mc_cs_o[1]),.CE2(1'b1),.WEn(mc_we_o) );

Hardware/Software co-design para aplicações de processamento de voz 176 A8Kx8 asram2 (.Address(mc_adr_o[12:0]),.dataIO(mc_dq[15: 8]),.OEn(mc_oe_o),.CE1n(mc_cs_o[1]),.CE2(1'b1),.WEn(mc_we_o) ); A8Kx8 asram3 (.Address(mc_adr_o[12:0]),.dataIO(mc_dq[ 7: 0]),.OEn(mc_oe_o),.CE1n(mc_cs_o[1]),.CE2(1'b1),.WEn(mc_we_o) ); // hookup wishbone master wb_master_model wbm(.clk(wb_clk),.rst(wb_rst),.adr(wb_adr_o),.din(wb_dat_i),.dout(wb_dat_o),.cyc(wb_cyc_o),.stb(wb_stb_o),.we(wb_we_o),.sel(wb_sel_o),.ack(wb_ack_i),.err(wb_err_i),.rty(wb_rty_i) ); // // testbench body // assign wb_rty_i = 1'b0; // no retries from memory controller // generate clock always #2.5 wb_clk <= ~wb_clk; always@(posedge wb_clk) // mc_clk <= #1 ~mc_clk; mc_clk <= #0 ~mc_clk; // initial statements initial begin wb_clk = 0; // start with low-level clock wb_rst = 1; // assert reset mc_clk = 0; sel_par = 1; // do not modify parity bits sel_pbus = 1; // use second SDRAMS set as parity sdrams repeat(20) @(posedge wb_clk); wb_rst = 0; // negate reset @(posedge wb_clk); run_tests; // show total errors detected wbm.show_tot_err_cnt; end $stop; ////////////////////// // // Internal tasks // task run_tests;

Hardware/Software co-design para aplicações de processamento de voz 177 begin prg_mc; // program memory controller BA-mask and CSR registers // force sdram0_3.debug = 1'b1; // turn on SDRAM debug option force sdram0_3.debug = 1'b0; // turn off SDRAM debug option /////////////// // SDRAM tests // tst_sdram_memfill; // test sdrams: Fill entire memory and verify // tst_sdram_parity; // test sdrams: Parity generation // tst_sdram_seq; // test sdrams: Fill-Verify, sequential access // tst_sdram_rnd; // test sdrams: Fill-Verify, random access // tst_sdram_rmw_seq; // test sdrams: Read-Modify-Write test, sequential access // tst_sdram_rmw_rnd; // test sdrams: Read-Modify-Write test, random access // tst_sdram_blk_cpy1; // test sdrams: Perform block copy, different src and dest. address // tst_sdram_blk_cpy2; // test sdrams: Perform block copy, src and dest same address // tst_sdram_bytes; // test sdrams: Peform byte accesses ////////////////////////////// // ASYNCHRONOUS MEMORIES TEST // tst_amem_seq; // test asynchronous memory // tst_amem_b2b; // test asynchronous memory back-2-back //////////////// // SSRAMS TESTS tst_ssram_seq; ////////////////////// // MULTI MEMORY TESTS // tst_blk_cpy1; // test block-copy: access sdrams + asrams // The next test (tst_blk_cyp2) is, saddly to say, useless. // It tests n-by-n situations for multiple SDRAMS, testing all possible settings for each SDRAM. // It is supposed to test the independence for each SDRAM chipselect. // However it is to time-consuming; it runs for about a month on an Athlon-XP 1800 system // tst_blk_cpy2; // test block-copy: access multiple sdrams ///////////////////////////// // EXTERNAL BUS MASTER TESTS // turn on external bus-master and rerun some tests // force ext_bm.on_off = 1'b1; // tst_sdram_seq; // test sdrams: Fill-Verify, sequential access // tst_amem_seq; // test asynchronous memory // tst_amem_b2b; // test asynchronous memory back-2-back // tst_blk_cpy1; // test block-copy: access sdrams + asrams end endtask // run_tests register task prg_mc; begin wbm.wb_write(0, 0, 32'h6000_0008, `BA_MASK); // program base address wbm.wb_write(0, 0, 32'h6000_0000, 32'h6000_0400); // program CSR // check written data wbm.wb_cmp(0, 0, 32'h6000_0008, `BA_MASK); wbm.wb_cmp(0, 0, 32'h6000_0000, 32'h6000_0400); end endtask //prg_mc

Hardware/Software co-design para aplicações de processamento de voz 178 //////////////////////////////// // Register test // task reg_test; begin end endtask // reg_test ///////////////////////// // include memory tests // // `include "tst_sdram.v" // `include "tst_asram.v" `include "tst_ssram.v" // `include "tst_multi_mem.v" endmodule

Hardware/Software co-design para aplicações de processamento de voz 179 Anexo 9: wb.vhd (descrição do master slave) -- Generated by PERL program wishbone.pl. Do not edit this file. -- -- For defines see wishbone.defines -- -- Generated Tue Jun 21 17:24:49 2005 -- -- Wishbone masters: -- wbm1 -- wbm2 -- -- Wishbone slaves: -- wbs1 -- baseadr 0x10000000 - size 0x00100000 -- baseadr 0x60000000 - size 0xffff -- baseadr 0x00000000 - size 0xffff ----------------------------------------------------------------------------------------- library IEEE; use IEEE.std_logic_1164.all; package intercon_package is function "and" ( l : std_logic_vector; r : std_logic) return std_logic_vector; end intercon_package; package body intercon_package is function "and" ( l : std_logic_vector; r : std_logic) return std_logic_vector is variable result : std_logic_vector(l'range); begin -- "and" for i in l'range loop result(i) := l(i) and r; end loop; -- i return result; end "and"; end intercon_package; library IEEE; use IEEE.std_logic_1164.all; entity trafic_supervision is generic ( priority : integer := 1; tot_priority : integer := 2); port ( bg : in std_logic; -- bus grant ce : in std_logic; -- clock enable trafic_limit : out std_logic; clk : in std_logic; reset : in std_logic); end trafic_supervision; architecture rtl of trafic_supervision is signal shreg : std_logic_vector(tot_priority-1 downto 0); signal cntr : integer range 0 to tot_priority; begin -- rtl -- purpose: holds information of usage of latest cycles -- type : sequential -- inputs : clk, reset, ce, bg -- outputs: shreg('left) sh_reg: process (clk) begin -- process shreg if clk'event and clk = '1' then -- rising clock edge

Hardware/Software co-design para aplicações de processamento de voz 180 if ce='1' then shreg <= shreg(tot_priority-2 downto 0) & bg; end if; end if; end process sh_reg; -- purpose: keeps track of used cycles -- type : sequential -- inputs : clk, reset, shreg('left), bg, ce -- outputs: trafic_limit counter: process (clk, reset) begin -- process counter if reset = '1' then -- asynchronous reset (active hi) cntr <= 0; trafic_limit <= '0'; elsif clk'event and clk = '1' then -- rising clock edge if ce='1' then if bg='1' and shreg(tot_priority-1)/='1' then cntr <= cntr + 1; if cntr=priority-1 then trafic_limit <= '1'; end if; elsif bg='0' and shreg(tot_priority-1)='1' then cntr <= cntr - 1; if cntr=priority then trafic_limit <= '0'; end if; end if; end if; end if; end process counter; end rtl; library IEEE; use IEEE.std_logic_1164.all; use work.intercon_package.all; entity intercon is port ( -- wishbone master port(s) -- wbm1 wbm1_dat_i : out std_logic_vector(31 downto 0); wbm1_ack_i : out std_logic; wbm1_err_i : out std_logic; wbm1_rty_i : out std_logic; wbm1_dat_o : in std_logic_vector(31 downto 0); wbm1_we_o : in std_logic; wbm1_sel_o : in std_logic_vector(3 downto 0); wbm1_adr_o : in std_logic_vector(31 downto 0); wbm1_cyc_o : in std_logic; wbm1_stb_o : in std_logic; -- wbm2 wbm2_dat_i : out std_logic_vector(31 downto 0); wbm2_ack_i : out std_logic; wbm2_err_i : out std_logic; wbm2_rty_i : out std_logic; wbm2_dat_o : in std_logic_vector(31 downto 0); wbm2_we_o : in std_logic; wbm2_sel_o : in std_logic_vector(3 downto 0); wbm2_adr_o : in std_logic_vector(31 downto 0); wbm2_cyc_o : in std_logic; wbm2_stb_o : in std_logic; -- wishbone slave port(s) -- wbs1 wbs1_dat_o : in std_logic_vector(31 downto 0); wbs1_ack_o : in std_logic; wbs1_err_o : in std_logic; wbs1_rty_o : in std_logic; wbs1_dat_i : out std_logic_vector(31 downto 0); wbs1_we_i : out std_logic; wbs1_sel_i : out std_logic_vector(3 downto 0); wbs1_adr_i : out std_logic_vector(31 downto 0); wbs1_cyc_i : out std_logic; wbs1_stb_i : out std_logic; -- clock and reset clk : in std_logic;

Hardware/Software co-design para aplicações de processamento de voz 181 reset : in std_logic); end intercon; architecture rtl of intercon is signal wbm1_bg : std_logic; -- bus grant signal wbm2_bg : std_logic; -- bus grant signal wbs1_ss : std_logic; -- slave select begin -- rtl arbiter_sharedbus: block signal wbm1_bg_1, wbm1_bg_2, wbm1_bg_q : std_logic; signal wbm2_bg_1, wbm2_bg_2, wbm2_bg_q : std_logic; signal wbm1_trafic_ctrl_limit : std_logic; signal wbm2_trafic_ctrl_limit : std_logic; signal ack, ce, idle :std_logic; begin -- arbiter ack <= wbs1_ack_o; trafic_supervision_1 : entity work.trafic_supervision generic map( priority => 5000, tot_priority => 10000) port map( bg => wbm1_bg, ce => ce, trafic_limit => wbm1_trafic_ctrl_limit, clk => clk, reset => reset); trafic_supervision_2 : entity work.trafic_supervision generic map( priority => 5000, tot_priority => 10000) port map( bg => wbm2_bg, ce => ce, trafic_limit => wbm2_trafic_ctrl_limit, clk => clk, reset => reset); process(clk,reset) begin if reset='1' then wbm1_bg_q <= '0'; elsif clk'event and clk='1' then if wbm1_bg_q='0' then wbm1_bg_q <= wbm1_bg; elsif ack='1' then wbm1_bg_q <= '0'; end if; end if; end process; process(clk,reset) begin if reset='1' then wbm2_bg_q <= '0'; elsif clk'event and clk='1' then if wbm2_bg_q='0' then wbm2_bg_q <= wbm2_bg; elsif ack='1' then wbm2_bg_q <= '0'; end if; end if; end process; idle <= '1' when wbm1_bg_q='0' and wbm2_bg_q='0' else '0'; wbm1_bg_1 <= '1' when idle='1' and wbm1_cyc_o='1' and wbm1_trafic_ctrl_limit='0' else '0'; wbm2_bg_1 <= '1' when idle='1' and wbm2_cyc_o='1' and wbm2_trafic_ctrl_limit='0' and (wbm1_bg_1='0') else '0'; wbm1_bg_2 <= '1' when idle='1' and (wbm1_bg_1='0' and wbm2_bg_1='0') and wbm1_cyc_o='1' else '0'; wbm2_bg_2 <= '1' when idle='1' and (wbm1_bg_1='0' and wbm2_bg_1='0' and wbm1_bg_2='0') and wbm2_cyc_o='1' else '0'; wbm1_bg <= wbm1_bg_q or wbm1_bg_1 or wbm1_bg_2; wbm2_bg <= wbm2_bg_q or wbm2_bg_1 or wbm2_bg_2; ce <= wbm1_cyc_o or wbm2_cyc_o when idle='1' else '0'; end block arbiter_sharedbus;

Hardware/Software co-design para aplicações de processamento de voz 182 decoder:block signal adr : std_logic_vector(31 downto 0); begin adr <= (wbm1_adr_o and wbm1_bg) or (wbm2_adr_o and wbm2_bg); wbs1_ss <= '1' when adr(31 downto 20)="000100000000" else '1' when adr(31 downto 16)="0110000000000000" else '1' when adr(31 downto 16)="0000000000000000" else '0'; wbs1_adr_i <= adr(31 downto 0); end block decoder; mux: block signal cyc, stb, we, ack : std_logic; signal sel : std_logic_vector(3 downto 0); signal dat_m2s, dat_s2m : std_logic_vector(31 downto 0); begin cyc <= (wbm1_cyc_o and wbm1_bg) or (wbm2_cyc_o and wbm2_bg); wbs1_cyc_i <= wbs1_ss and cyc; stb <= (wbm1_stb_o and wbm1_bg) or (wbm2_stb_o and wbm2_bg); wbs1_stb_i <= stb; we <= (wbm1_we_o and wbm1_bg) or (wbm2_we_o and wbm2_bg); wbs1_we_i <= we; ack <= wbs1_ack_o; wbm1_ack_i <= ack and wbm1_bg; wbm2_ack_i <= ack and wbm2_bg; wbm1_rty_i <= wbs1_rty_o; wbm2_rty_i <= wbs1_rty_o; wbm1_err_i <= wbs1_err_o; wbm2_err_i <= wbs1_err_o; sel <= (wbm1_sel_o and wbm1_bg) or (wbm2_sel_o and wbm2_bg); wbs1_sel_i <= sel; dat_m2s <= (wbm1_dat_o and wbm1_bg) or (wbm2_dat_o and wbm2_bg); wbs1_dat_i <= dat_m2s; dat_s2m <= (wbs1_dat_o and wbs1_ss); wbm1_dat_i <= dat_s2m; wbm2_dat_i <= dat_s2m; end block mux; end rtl;

Hardware/Software co-design para aplicações de processamento de voz 183 Anexo 10: Wishone.defines (definições para a elaboração do master slave) # Generated by PERL program wishbone.pl. # File used as input for wishbone arbiter generation # Generated Wed Jun 22 01:23:19 2005 filename=wb intercon=intercon syscon=syscon target=xilinx hdl=vhdl signal_groups=0 tga_bits=2 tgc_bits=3 tgd_bits=0 rename_tga=bte rename_tgc=cti rename_tgd=tgd classic=000 endofburst=111 dat_size=32 adr_size=32 mux_type=andor interconnect=sharedbus master wbm1 type=rw lock_o=0 tga_o=0 tgc_o=0 tgd_o=0 err_i=1 rty_i=1 priority=30 end master wbm1 master wbm2 type=rw lock_o=0 tga_o=0 tgc_o=0 tgd_o=0 err_i=1 rty_i=1 priority=5 end master wbm2 master wbm3 type=rw lock_o=0 tga_o=0 tgc_o=0 tgd_o=0 err_i=1 rty_i=1 priority=20 end master wbm3 slave wbs1 type=rw adr_i_hi=31 adr_i_lo=0 tga_i=0 tgc_i=0 tgd_i=0 lock_i=0 err_o=1 rty_o=1 baseadr=0x00000000 size=0xffff baseadr1=0x60000000 size1=0xffff baseadr2=0x10000000 size2=0xfffff end slave wbs1

Hardware/Software co-design para aplicações de processamento de voz 184 Anexo 11: Modelo verilog da memória da Cypress /**************************************************************************************** * * File Name: CY7C1340G_PL_DCD.v * Version: 1.0 * Date: July 26th, 2004 * Model: BUS Functional * Simulator: Verilog-XL (CADENCE) * * * Queries: MPD Applications * Website: www.cypress.com/support * Company: Cypress Semiconductor * Part #: CY7C1340G (128K x 32) * * Description: Cypress 4Mb Synburst SRAM (Pipelined DCD) * * * Disclaimer: THESE DESIGNS ARE PROVIDED "AS IS" WITH NO WARRANTY * WHATSOEVER AND CYPRESS SPECIFICALLY DISCLAIMS ANY * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR * A PARTICULAR PURPOSE, OR AGAINST INFRINGEMENT. * * Copyright(c) Cypress Semiconductor, 2004 * All rights reserved * * Rev Date Changes * --- ---------- --------------------------------------- * 1.0 07/26/2004 - New Model * - New Testbench * - New test vector * ****************************************************************************************/ // DO NOT CHANGE THE TIMESCALE // MAKE SURE YOUR SIMULATOR USE "PS" RESOLUTION `timescale 1ns / 10ps // Timings for Different Speed Bins (sb): 250MHz, 225MHz, 200MHz, 167MHz, 133MHz, 100MHz `define sb250 `ifdef sb250 `define tco 2.6 // Data Output Valid After CLK Rise `define tcyc 4.0 // Clock cycle time `define tch 1.7 // Clock HIGH time `define tcl 1.7 // Clock LOW time `define tchz 2.6 // Clock to High-Z `define tclz 0.0 // Clock to Low-Z `define toehz 2.6 // OE# HIGH to Output High-Z `define toelz 0.0 // OE# LOW to Output Low-Z `define toev 2.6 // OE# LOW to Output Valid Rise `endif `define tas 1.2 // Address Set-up Before CLK Rise `define tads 1.2 // ADSC#, ADSP# Set-up Before CLK Rise `define tadvs 1.2 // ADV# Set-up Before CLK Rise `define twes 1.2 // BWx#, GW#, BWE# Set-up Before CLK `define tds 1.2 // Data Input Set-up Before CLK Rise `define tces 1.2 // Chip Enable Set-up `define tah 0.3 // Address Hold After CLK Rise `define tadh 0.3 // ADSC#, ADSP# Hold After CLK Rise `define tadvh 0.3 // ADV# Hold After CLK Rise `define tweh 0.3 // BWx#, GW#, BWE# Hold After CLK Rise `define tdh 0.3 // Data Input Hold After CLK Rise `define tceh 0.3 // Chip Enable Hold After CLK Rise `ifdef sb225 `define tco 2.6 // Data Output Valid After CLK Rise `define tcyc 4.4 // Clock cycle time `define tch 2.0 // Clock HIGH time

Hardware/Software co-design para aplicações de processamento de voz 185 `define tcl 2.0 // Clock LOW time `define tchz 2.6 // Clock to High-Z `define tclz 0.0 // Clock to Low-Z `define toehz 2.6 // OE# HIGH to Output High-Z `define toelz 0.0 // OE# LOW to Output Low-Z `define toev 2.6 // OE# LOW to Output Valid `define tas 1.2 // Address Set-up Before CLK Rise `define tads 1.2 // ADSC#, ADSP# Set-up Before CLK Rise `define tadvs 1.2 // ADV# Set-up Before CLK Rise `define twes 1.2 // BWx#, GW#, BWE# Set-up Before CLK Rise `define tds 1.2 // Data Input Set-up Before CLK Rise `define tces 1.2 // Chip Enable Set-up `endif `define tah 0.5 // Address Hold After CLK Rise `define tadh 0.5 // ADSC#, ADSP# Hold After CLK Rise `define tadvh 0.5 // ADV# Hold After CLK Rise `define tweh 0.5 // BWx#, GW#, BWE# Hold After CLK Rise `define tdh 0.5 // Data Input Hold After CLK Rise `define tceh 0.5 // Chip Enable Hold After CLK Rise `ifdef sb200 `define tco 2.8 // Data Output Valid After CLK Rise `define tcyc 5.0 // Clock cycle time `define tch 2.0 // Clock HIGH time `define tcl 2.0 // Clock LOW time `define tchz 2.8 // Clock to High-Z `define tclz 0.0 // Clock to Low-Z `define toehz 2.8 // OE# HIGH to Output High-Z `define toelz 0.0 // OE# LOW to Output Low-Z `define toev 2.8 // OE# LOW to Output Valid `define tas 1.2 // Address Set-up Before CLK Rise `define tads 1.2 // ADSC#, ADSP# Set-up Before CLK Rise `define tadvs 1.2 // ADV# Set-up Before CLK Rise `define twes 1.2 // BWx#, GW#, BWE# Set-up Before CLK Rise `define tds 1.2 // Data Input Set-up Before CLK Rise `define tces 1.2 // Chip Enable Set-up `endif `define tah 0.5 // Address Hold After CLK Rise `define tadh 0.5 // ADSC#, ADSP# Hold After CLK Rise `define tadvh 0.5 // ADV# Hold After CLK Rise `define tweh 0.5 // BWx#, GW#, BWE# Hold After CLK Rise `define tdh 0.5 // Data Input Hold After CLK Rise `define tceh 0.5 // Chip Enable Hold After CLK Rise `ifdef sb167 `define tco 3.5 // Data Output Valid After CLK Rise `define tcyc 6.0 // Clock cycle time `define tch 2.5 // Clock HIGH time `define tcl 2.5 // Clock LOW time `define tchz 3.5 // Clock to High-Z `define tclz 0.0 // Clock to Low-Z `define toehz 3.5 // OE# HIGH to Output High-Z `define toelz 0.0 // OE# LOW to Output Low-Z `define toev 3.5 // OE# LOW to Output Valid `define tas 1.5 // Address Set-up Before CLK Rise `define tads 1.5 // ADSC#, ADSP# Set-up Before CLK Rise `define tadvs 1.5 // ADV# Set-up Before CLK Rise `define twes 1.5 // BWx#, GW#, BWE# Set-up Before CLK Rise `define tds 1.5 // Data Input Set-up Before CLK Rise `define tces 1.5 // Chip Enable Set-up `define tah 0.5 // Address Hold After CLK Rise `define tadh 0.5 // ADSC#, ADSP# Hold After CLK Rise `define tadvh 0.5 // ADV# Hold After CLK Rise `define tweh 0.5 // BWx#, GW#, BWE# Hold After CLK Rise `define tdh 0.5 // Data Input Hold After CLK Rise `define tceh 0.5 // Chip Enable Hold After CLK Rise

Hardware/Software co-design para aplicações de processamento de voz 186 `endif `ifdef sb133 `define tco 4.0 // Data Output Valid After CLK Rise `define tcyc 7.5 // Clock cycle time `define tch 3.0 // Clock HIGH time `define tcl 3.0 // Clock LOW time `define tchz 4.0 // Clock to High-Z `define tclz 0.0 // Clock to Low-Z `define toehz 4.0 // OE# HIGH to Output High-Z `define toelz 0.0 // OE# LOW to Output Low-Z `define toev 4.0 // OE# LOW to Output Valid `define tas 1.5 // Address Set-up Before CLK Rise `define tads 1.5 // ADSC#, ADSP# Set-up Before CLK Rise `define tadvs 1.5 // ADV# Set-up Before CLK Rise `define twes 1.5 // BWx#, GW#, BWE# Set-up Before CLK Rise `define tds 1.5 // Data Input Set-up Before CLK Rise `define tces 1.5 // Chip Enable Set-up `endif `define tah 0.5 // Address Hold After CLK Rise `define tadh 0.5 // ADSC#, ADSP# Hold After CLK Rise `define tadvh 0.5 // ADV# Hold After CLK Rise `define tweh 0.5 // BWx#, GW#, BWE# Hold After CLK Rise `define tdh 0.5 // Data Input Hold After CLK Rise `define tceh 0.5 // Chip Enable Hold After CLK Rise `ifdef sb100 `define tco 4.5 // Data Output Valid After CLK Rise `define tcyc 10.0 // Clock cycle time `define tch 3.5 // Clock HIGH time `define tcl 3.5 // Clock LOW time `define tchz 4.5 // Clock to High-Z `define tclz 0.0 // Clock to Low-Z `define toehz 4.5 // OE# HIGH to Output High-Z `define toelz 0.0 // OE# LOW to Output Low-Z `define toev 4.5 // OE# LOW to Output Valid `define tas 1.5 // Address Set-up Before CLK Rise `define tads 1.5 // ADSC#, ADSP# Set-up Before CLK Rise `define tadvs 1.5 // ADV# Set-up Before CLK Rise `define twes 1.5 // BWx#, GW#, BWE# Set-up Before CLK Rise `define tds 1.5 // Data Input Set-up Before CLK Rise `define tces 1.5 // Chip Enable Set-up `endif `define tah 0.5 // Address Hold After CLK Rise `define tadh 0.5 // ADSC#, ADSP# Hold After CLK Rise `define tadvh 0.5 // ADV# Hold After CLK Rise `define tweh 0.5 // BWx#, GW#, BWE# Hold After CLK Rise `define tdh 0.5 // Data Input Hold After CLK Rise `define tceh 0.5 // Chip Enable Hold After CLK Rise module CY7C1340G_PLDCD (ZZ, Mode, ADDR, GW_N, BWE_N, BWd_N, BWc_N, BWb_N, BWa_N, CE1_N, CE2, CE3_N, ADSP_N, ADSC_N, ADV_N, OE_N, DQ, CLK); parameter addr_bits = 17; // 17 bits parameter data_bits = 32; // 32 bits parameter mem_sizes = 131072; // 128K inout [(data_bits - 1) : 0] DQ; // Data IO input [(addr_bits - 1) : 0] ADDR; // ADDRess input Mode; // Burst Mode input ADV_N; // Synchronous ADDRess Advance input CLK; // Clock input ADSC_N; // Synchronous ADDRess Status Controller input ADSP_N; // Synchronous ADDRess Status Processor

Hardware/Software co-design para aplicações de processamento de voz 187 input BWa_N; // Synchronous Byte Write Enables input BWb_N; // Synchronous Byte Write Enables input BWc_N; // Synchronous Byte Write Enables input BWd_N; // Synchronous Byte Write Enables input BWE_N; // Byte Write Enable input GW_N; // Global Write input CE1_N; // Synchronous Chip Enable input CE2; // Synchronous Chip Enable input CE3_N; // Synchronous Chip Enable input OE_N; // Output Enable input ZZ; // Snooze Mode reg [((data_bits / 4) - 1) : 0] bank0 [0 : mem_sizes]; // Memory Bank 0 reg [((data_bits / 4) - 1) : 0] bank1 [0 : mem_sizes]; // Memory Bank 1 reg [((data_bits / 4) - 1) : 0] bank2 [0 : mem_sizes]; // Memory Bank 2 reg [((data_bits / 4) - 1) : 0] bank3 [0 : mem_sizes]; // Memory Bank 3 reg [(data_bits - 1) : 0] din; // Data In reg [(data_bits - 1) : 0] dout; // Data Out reg [(addr_bits - 1) : 0] addr_reg_in; // ADDRess Register In reg [(addr_bits - 1) : 0] addr_reg_read; // ADDRess Register for Read reg [(addr_bits - 1) : 0] addr_reg_write; // ADDRess Register for Write reg [1 : 0] bcount; // 2-bit Burst Counter reg [1 : 0] first_addr; // 2-bit Burst Counter reg reg reg reg reg reg reg reg reg reg reg reg reg reg reg reg ce_reg; Read_reg; Read_reg_o; WrN_reg; ADSP_N_o; pipe_reg; bwa_reg; bwb_reg; bwc_reg; bwd_reg; Sys_clk; test; pcsr_write; ctlr_write; latch_addr_current; latch_addr_old; wire ce = (~CE1_N & CE2 & ~CE3_N); wire Write_n = ~(((~BWa_N ~BWb_N ~BWc_N ~BWd_N) & ~BWE_N) ~GW_N ) ; wire Read = (((BWa_N & BWb_N & BWc_N & BWd_N) & ~BWE_N) (GW_N & BWE_N) (~ADSP_N & ce)) ; wire bwa_n = ~(~Write_n & (~GW_N (~BWE_N & ~BWa_N ))); wire bwb_n = ~(~Write_n & (~GW_N (~BWE_N & ~BWb_N ))); wire bwc_n = ~(~Write_n & (~GW_N (~BWE_N & ~BWc_N ))); wire bwd_n = ~(~Write_n & (~GW_N (~BWE_N & ~BWd_N ))); wire latch_addr = (~ADSC_N (~ADSP_N & ~CE1_N)); wire #`toehz OeN_HZ = OE_N? 1 : 0; wire #`toev OeN_DataValid = ~OE_N? 0 : 1; wire OeN_efct = ~OE_N? OeN_DataValid : OeN_HZ; wire #`tchz WR_HZ = WrN_reg? 1 : 0; wire #`tclz WR_LZ = ~WrN_reg? 0 : 1; wire WR_efct = ~WrN_reg? WR_LZ : WR_HZ; wire #`tchz CE_HZ = ~pipe_reg? 0 : 1 ; wire #`tclz CE_LZ = pipe_reg? 1 : 0 ;

Hardware/Software co-design para aplicações de processamento de voz 188 wire Pipe_efct = pipe_reg? CE_LZ : CE_HZ ; wire #`tchz RD_HZ = ~Read_reg_o? 0 : 1 ; wire #`tclz RD_LZ = Read_reg_o? 1 : 0 ; wire RD_efct = Read_reg_o? CE_LZ : CE_HZ ; // Initialize initial begin ce_reg = 1'b0; pipe_reg = 1'b0; Sys_clk = 1'b0; $timeformat (-9, 1, " ns", 10); $readmemh("banco0.mem",bank0); $readmemh("banco1.mem",bank1); $readmemh("banco2.mem",bank2); $readmemh("banco3.mem",bank3); // Format time unit end // System Clock Decode always begin @ (posedge CLK) begin Sys_clk = ~ZZ; end @ (negedge CLK) begin Sys_clk = 1'b0; end end always @ (posedge Sys_clk) begin // Read Register if (~Write_n) Read_reg_o = 1'b0; else Read_reg_o = Read_reg; if (~Write_n) Read_reg = 1'b0; else Read_reg = Read; if (Read_reg == 1'b1) begin end pcsr_write ctlr_write = 1'b0; = 1'b0; // Write Register if (Read_reg_o == 1'b1) else WrN_reg = Write_n; WrN_reg = 1'b1; latch_addr_old = latch_addr_current; latch_addr_current = latch_addr; if (latch_addr_old == 1'b1 & ~Write_n & ADSP_N_o == 1'b0) pcsr_write = 1'b1; //Ctlr Write = 0; Pcsr Write = 1; else if (latch_addr_current == 1'b1 & ~Write_n & ADSP_N & ~ADSC_N) ctlr_write = 1'b1; //Ctlr Write = 0; Pcsr Write = 1; // ADDRess Register if (latch_addr) begin addr_reg_in = ADDR; bcount = ADDR [1 : 0]; first_addr = ADDR [1 : 0]; end // ADSP_N Previous-Cycle Register ADSP_N_o <= ADSP_N; // Binary Counter and Logic if (~Mode & ~ADV_N & ~latch_addr) bcount = (bcount + 1); // Linear Burst // Advance Counter

Hardware/Software co-design para aplicações de processamento de voz 189 else if (Mode & ~ADV_N & ~latch_addr) begin if (first_addr % 2 == 0) bcount = (bcount + 1); else if (first_addr % 2 == 1) bcount = (bcount - 1); end // Interleaved Burst // Increment Counter // Decrement Counter // Read ADDRess addr_reg_read = addr_reg_write; // Write ADDRess addr_reg_write = {addr_reg_in [(addr_bits - 1) : 2], bcount[1], bcount[0]; // Byte Write Register bwa_reg = ~bwa_n; bwb_reg = ~bwb_n; bwc_reg = ~bwc_n; bwd_reg = ~bwd_n; // Enable Register pipe_reg = ce_reg; // Enable Register if (latch_addr) ce_reg = ce; // Input Register if (ce_reg & (~bwa_n ~bwb_n ~bwc_n ~bwd_n) & (pcsr_write ctlr_write)) begin din = DQ; end // Byte Write Driver if (ce_reg & bwa_reg) begin bank0 [addr_reg_write] = din [ 7 : 0]; end if (ce_reg & bwb_reg) begin bank1 [addr_reg_write] = din [15 : 8]; end if (ce_reg & bwc_reg) begin bank2 [addr_reg_write] = din [23 : 16]; end if (ce_reg & bwd_reg) begin bank3 [addr_reg_write] = din [31 : 24]; end // Output Registers if (~Write_n pipe_reg == 1'b0) dout [ 31 : 0] <= #`tchz 32'bZ; else if (Read_reg_o == 1'b1) begin dout [ 7 : 0] <= #`tco bank0 [addr_reg_read]; dout [15 : 8] <= #`tco bank1 [addr_reg_read]; dout [23 : 16] <= #`tco bank2 [addr_reg_read]; dout [31 : 24] <= #`tco bank3 [addr_reg_read]; end end // Output Buffers assign DQ = (~OE_N & ~ZZ & Pipe_efct & RD_efct & WR_efct)? dout : 32'bz; // Timing Check specify $width (negedge CLK, `tcl); $width (posedge CLK, `tch); $period (negedge CLK, `tcyc); $period (posedge CLK, `tcyc); $setuphold (posedge CLK, ADSP_N, `tads, `tadh); $setuphold (posedge CLK, ADSC_N, `tads, `tadh); $setuphold (posedge CLK, ADDR, `tas, `tah); $setuphold (posedge CLK, BWa_N, `twes, `tweh); $setuphold (posedge CLK, BWb_N, `twes, `tweh); $setuphold (posedge CLK, BWc_N, `twes, `tweh);

Hardware/Software co-design para aplicações de processamento de voz 190 $setuphold (posedge CLK, BWd_N, `twes, `tweh); $setuphold (posedge CLK, BWE_N, `twes, `tweh); $setuphold (posedge CLK, GW_N, `twes, `tweh); $setuphold (posedge CLK, CE1_N, `tces, `tceh); $setuphold (posedge CLK, CE2, `tces, `tceh); $setuphold (posedge CLK, CE3_N, `tces, `tceh); $setuphold (posedge CLK, ADV_N, `tadvs, `tadvh); endspecify endmodule

Hardware/Software co-design para aplicações de processamento de voz 191 Anexo 12: Modelo verilog da memória da Micron /**************************************************************************************** * * File Name: MT58L1MY18D.V * Version: 1.3 * Date: March 8th, 1999 * Model: BUS Functional * Simulator: Model Technology * * Dependencies: None * * Author: Son P. Huynh * Email: sphuynh@micron.com * Phone: (208) 368-3825 * Company: Micron Technology, Inc. * Part #: MT58L1MY18D (1Mb x 18) * * Description: This is Micron's Syncburst SRAM (Pipelined DCD) * * Limitation: * * Disclaimer: THESE DESIGNS ARE PROVIDED "AS IS" WITH NO WARRANTY * WHATSOEVER AND MICRON SPECIFICALLY DISCLAIMS ANY * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR * A PARTICULAR PURPOSE, OR AGAINST INFRINGEMENT. * * Copyright (c) 1997 Micron Semiconductor Products, Inc. * All rights researved * * Rev Author Date Changes * --- ---------------------------- ---------- --------------------------------------- * 1.3 Son P. Huynh 208-368-3825 03/08/1999 Improve model functionality * Micron Technology, Inc. * ****************************************************************************************/ // DO NOT CHANGE THE TIMESCALE // MAKE SURE YOUR SIMULATOR USE "PS" RESOLUTION `timescale 1ns / 100ps module mt58l1my18d (Dq, Addr, Mode, Adv_n, Clk, Adsc_n, Adsp_n, Bwa_n, Bwb_n, Bwe_n, Gw_n, Ce_n, Ce2, Ce2_n, Oe_n, Zz); parameter addr_bits = 20; // 20 bits parameter data_bits = 18; // 18 bits parameter mem_sizes = 1048575; // 1 Mb parameter reg_delay = 0.1; // 100 ps parameter out_delay = 0.1; // 100 ps parameter tkqhz = 3.5; // -6 device inout [(data_bits - 1) : 0] Dq; // Data IO input [(addr_bits - 1) : 0] Addr; // Address input Mode; // Burst Mode input Adv_n; // Synchronous Address Advance input Clk; // Clock input Adsc_n; // Synchronous Address Status Controller input Adsp_n; // Synchronous Address Status Processor input Bwa_n; // Synchronous Byte Write Enables input Bwb_n; // Synchronous Byte Write Enables input Bwe_n; // Byte Write Enable input Gw_n; // Global Write input Ce_n; // Synchronous Chip Enable input Ce2; // Synchronous Chip Enable input Ce2_n; // Synchronous Chip Enable input Oe_n; // Output Enable input Zz; // Snooze Mode reg [((data_bits / 2) - 1) : 0] bank0 [0 : mem_sizes]; // Memory Bank 0 reg [((data_bits / 2) - 1) : 0] bank1 [0 : mem_sizes]; // Memory Bank 1

Hardware/Software co-design para aplicações de processamento de voz 192 reg [(data_bits - 1) : 0] din; // Input Registers reg [(data_bits - 1) : 0] dout; // Output Registers reg [(addr_bits - 1) : 0] addr_reg_in; // Address Register In reg [(addr_bits - 1) : 0] addr_reg_read; // Address Register for Read Operation reg [1 : 0] bcount; // 2-bit Burst Counter reg reg reg reg reg ce_reg; pipe_reg; bwa_reg; bwb_reg; sys_clk; wire ce = (~Ce_n & ~Ce2_n & Ce2); wire bwa_n = (((Bwa_n Bwe_n) & Gw_n) (~Ce_n & ~Adsp_n)); wire bwb_n = (((Bwb_n Bwe_n) & Gw_n) (~Ce_n & ~Adsp_n)); wire clr = (~Adsc_n (~Adsp_n & ~Ce_n)); wire [(addr_bits - 1) : 0] addr_reg_write; // Address Register for Write Operation wire baddr1; // Burst Address 1 wire baddr0; // Burst Address 0 // Initialize initial begin ce_reg = 1'b0; sys_clk = 1'b0; pipe_reg = 1'b0; $timeformat (-9, 1, " ns", 10); end // Format time unit task mem_fill; input x; integer a, n, x; begin a=0; for(n=0;n<x;n=n+1) begin bank0[n] = a; bank1[n] = a+1; a=a+2; end end endtask // System Clock always begin @ (posedge Clk) begin sys_clk = ~Zz; end @ (negedge Clk) begin sys_clk = 1'b0; end end always @ (posedge sys_clk) begin // Address Register if (clr) addr_reg_in <= Addr; addr_reg_read <= {addr_reg_in [(addr_bits - 1) : 2], baddr1, baddr0; // Binary Counter and Logic if ( Mode & clr) bcount <= 0; // Interleaved Burst else if (~Mode & clr) bcount <= Addr [1 : 0]; // Linear Burst else if (~Adv_n & ~clr) bcount <= (bcount + 1); // Advance Counter // Byte Write Register bwa_reg <= ~bwa_n; bwb_reg <= ~bwb_n;

Hardware/Software co-design para aplicações de processamento de voz 193 end // Enable Register if (clr) ce_reg <= ce; // Pipelined Enable pipe_reg <= ce_reg; // Burst Address Decode assign addr_reg_write = {addr_reg_in [(addr_bits - 1) : 2], baddr1, baddr0; assign baddr1 = Mode? (bcount [1] ^ addr_reg_in [1]) : bcount [1]; assign baddr0 = Mode? (bcount [0] ^ addr_reg_in [0]) : bcount [0]; // Write Driver always @ (posedge Clk) begin #reg_delay; if (ce_reg & bwa_reg) begin din [data_bits / 2-1 : 0] <= Dq [data_bits / 2-1 : 0]; bank0 [addr_reg_write] <= Dq [data_bits / 2-1 : 0]; end if (ce_reg & bwb_reg) begin din [data_bits - 1 : data_bits / 2] <= Dq [data_bits - 1 : data_bits / 2]; bank1 [addr_reg_write] <= Dq [data_bits - 1 : data_bits / 2]; end end // Output Registers always @ (posedge Clk) begin #out_delay; if (~(bwa_reg bwb_reg)) begin dout [data_bits / 2-1 : 0] <= bank0 [addr_reg_read]; dout [data_bits - 1 : data_bits / 2] <= bank1 [addr_reg_read]; end else begin dout [data_bits - 1 : 0] <= {data_bits{1'bz; end end // Output Buffers assign #(tkqhz) Dq = (~Oe_n & ~Zz & pipe_reg & ~(bwa_reg bwb_reg))? dout : {data_bits{1'bz; // Timing Check (6 ns clock cycle / 166 MHz) // Please download latest datasheet from our Web site: // http://www.micron.com/mti specify specparam tkc = 6.0, // Clock - Clock cycle time tkh = 2.3, // Clock HIGH time tkl = 2.3, // Clock LOW time tas = 1.5, // Setup Times - Address tadss = 1.5, // Address Status taas = 1.5, // Address Advance tws = 1.5, // Byte Write Enables tds = 1.5, // Data-in tces = 1.5, // Chip Enable tah = 0.5, // Hold Times - Address tadsh = 0.5, // Address Status taah = 0.5, // Address Advance twh = 0.5, // Byte Write Enables tdh = 0.5, // Data-in tceh = 0.5; // Chip Enable $width (negedge Clk, tkl); $width (posedge Clk, tkh); $period (negedge Clk, tkc); $period (posedge Clk, tkc); $setuphold (posedge Clk, Adsp_n, tadss, tadsh); $setuphold (posedge Clk, Adsc_n, tadss, tadsh); $setuphold (posedge Clk, Addr, tas, tah); $setuphold (posedge Clk, Bwa_n, tws, twh); $setuphold (posedge Clk, Bwb_n, tws, twh); $setuphold (posedge Clk, Bwe_n, tws, twh); $setuphold (posedge Clk, Gw_n, tws, twh); $setuphold (posedge Clk, Ce_n, tces, tceh); $setuphold (posedge Clk, Ce2, tces, tceh); $setuphold (posedge Clk, Ce2_n, tces, tceh); $setuphold (posedge Clk, Adv_n, taas, taah); endspecify

Hardware/Software co-design para aplicações de processamento de voz 194 endmodule

Hardware/Software co-design para aplicações de processamento de voz 195 Anexo 13: bench1.v ///////////////////////////////////////////////////////////////////// //// //// //// OpenCores Memory Controller Testbench //// //// Main testbench //// //// //// //// Author: Richard Herveille //// //// richard@asics.ws //// //// //// //// //// //// Downloaded from: http://www.opencores.org/cores/mem_ctrl/ //// //// //// ///////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2001 Richard Herveille //// //// richard@asics.ws //// //// //// //// This source file may be used and distributed without //// //// restriction provided that this copyright statement is not //// //// removed from the file and that any derivative work contains //// //// the original copyright notice and the associated disclaimer.//// //// //// //// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// //// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// //// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// //// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// //// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// //// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// //// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// //// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// //// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// //// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// //// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// //// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// //// POSSIBILITY OF SUCH DAMAGE. //// //// //// ///////////////////////////////////////////////////////////////////// // ToDo: // 1) add power-on configuration // 2) test SSRAM // 3) test synchronous devices??? // // CVS Log // // $Id: bench.v,v 1.1 2002/03/06 15:10:34 rherveille Exp $ // // $Date: 2002/03/06 15:10:34 $ // $Revision: 1.1 $ // $Author: rherveille $ // $Locker: $ // $State: Exp $ // // Change History: // $Log: bench.v,v $ // Revision 1.1 2002/03/06 15:10:34 rherveille // Initial release // // `include "timescale.v" `define SDRAM_ROWA_HI 12 // row address hi-bit `define SDRAM_COLA_HI 8 // column address hi-bit `define BA_MASK 32'h0000_00e0 // base address mask `define SDRAM1_LOC 32'h0400_0000 // location of sdram1 in address-space `define SDRAM2_LOC 32'h0800_0000 // location of sdram2 in address-space `define SRAM_LOC 32'h0C00_0000 // location of srams in address-space `define SSRAM_LOC 32'h1000_0000 // location of ssrams in address-space module super_bench_top();

Hardware/Software co-design para aplicações de processamento de voz 196 // // internal wires // reg wb_clk; reg mc_clk; reg wb_rst; wire [31:0] wb_dat_i, wb_dat_o; wire [31:0] wb_adr_o; wire wb_cyc_o, wb_stb_o; wire [ 3:0] wb_sel_o; wire wb_ack_i, wb_err_i, wb_rty_i; wire wb_mc_stb; wire [23:0] mc_adr_o; wire [31:0] mc_dq, mc_dq_o; wire [ 3:0] mc_dp, mc_dp_o, pbus_o, pbus_i; reg [ 3:0] set_par; wire [31:0] par_con; reg sel_par, sel_pbus; wire par_sdram_cs; wire mc_doe_o; wire [ 3:0] mc_dqm_o; wire mc_we_o, mc_oe_o; wire mc_ras_o, mc_cas_o, mc_cke_o; wire [ 7:0] mc_cs_o; wire mc_pad_oe; wire mc_adsc_o, mc_adv_o, mc_zz_o; // ssram connections wire ext_br, ext_bg; /******************* wires needed to connect master1 to master/slave block*******/ wire [31:0] wbm1_dat_o; wire wbm1_ack_o ; wire wbm1_err_o ; wire wbm1_rty_o ; wire [31:0] wbm1_dat_i ; wire wbm1_we_i ; wire [3:0] wbm1_sel_i ; wire [31:0] wbm1_adr_i ; wire wbm1_cyc_i ; wire wbm1_stb_i ; /******************************************************************************/ /******************* wires needed to connect master2 to master/slave block*******/ wire [31:0] wbm2_dat_o; wire wbm2_ack_o ; wire wbm2_err_o ; wire wbm2_rty_o ; wire [31:0] wbm2_dat_i ; wire wbm2_we_i ; wire [3:0] wbm2_sel_i ; wire [31:0] wbm2_adr_i ; wire wbm2_cyc_i ; wire wbm2_stb_i ; real a ; /******************************************************************************/

Hardware/Software co-design para aplicações de processamento de voz 197 // // hookup modules // // hookup watch-dog counter /*watch_dog #(1024) wdog (.clk(wb_clk),.cyc_i(wbm1_cyc_i),.ack_i(wbm1_ack_o),.adr_i(wbm1_adr_i) ); watch_dog #(1024) wdog2 (.clk(wb_clk),.cyc_i(wbm2_cyc_i),.ack_i(wbm2_ack_o),.adr_i(wbm2_adr_i) );*/ // hookup external bus-master model bm_model ext_bm(.br(ext_br),.bg(ext_bg),.chk(mc_pad_oe) ); // hookup ERR checker err_check err_chk(wbm1_err_o, sel_par); // hookup CSn checker cs_check cs_chec(mc_cs_o); intercon master_slave( /* -- wishbone master port(s) -- wbm1*/.wbm1_dat_i(wbm1_dat_o),.wbm1_ack_i(wbm1_ack_o),.wbm1_err_i(wbm1_err_o), //.wbm1_rty_i(),.wbm1_dat_o(wbm1_dat_i),.wbm1_we_o(wbm1_we_i),.wbm1_sel_o(wbm1_sel_i),.wbm1_adr_o(wbm1_adr_i),.wbm1_cyc_o(wbm1_cyc_i),.wbm1_stb_o(wbm1_stb_i), /* -- wbm2 */.wbm2_dat_i(wbm2_dat_o),.wbm2_ack_i(wbm2_ack_o),.wbm2_err_i(wbm2_err_o), //.wbm2_rty_i(),.wbm2_dat_o(wbm2_dat_i),.wbm2_we_o(wbm2_we_i),.wbm2_sel_o(wbm2_sel_i),.wbm2_adr_o(wbm2_adr_i),.wbm2_cyc_o(wbm2_cyc_i),.wbm2_stb_o(wbm2_stb_i), /* wishbone slave port(s) wbs1 */.wbs1_dat_o(wb_dat_i),.wbs1_ack_o(wb_ack_i),.wbs1_err_o(wb_err_i),

Hardware/Software co-design para aplicações de processamento de voz 198.wbs1_rty_o(1'b0),.wbs1_dat_i(wb_dat_o),.wbs1_we_i (wb_we_o),.wbs1_sel_i(wb_sel_o),.wbs1_adr_i(wb_adr_o),.wbs1_cyc_i(wb_cyc_o),.wbs1_stb_i(wb_stb_o),.clk (wb_clk),.reset (wb_rst ) ); // hookup memory controller mc_top dut ( // wishbone interface.clk_i(wb_clk),.rst_i(wb_rst),.wb_data_i(wb_dat_o),.wb_data_o(wb_dat_i),.wb_addr_i(wb_adr_o),.wb_sel_i(wb_sel_o),.wb_we_i(wb_we_o),.wb_cyc_i(wb_cyc_o),.wb_stb_i(wb_stb_o),.wb_ack_o(wb_ack_i),.wb_err_o(wb_err_i), ); // memory controller.susp_req_i(1'b0),.resume_req_i(1'b0),.suspended_o(),.poc_o(),.mc_clk_i(mc_clk),.mc_br_pad_i(ext_br),.mc_bg_pad_o(ext_bg),.mc_ack_pad_i(1'b0),.mc_addr_pad_o(mc_adr_o),.mc_data_pad_i(mc_dq),.mc_data_pad_o(mc_dq_o),.mc_dp_pad_i(pbus_i), // attach parity bus.mc_dp_pad_o(mc_dp_o),.mc_doe_pad_doe_o(mc_doe_o),.mc_dqm_pad_o(mc_dqm_o),.mc_oe_pad_o_(mc_oe_o),.mc_we_pad_o_(mc_we_o),.mc_cas_pad_o_(mc_cas_o),.mc_ras_pad_o_(mc_ras_o),.mc_cke_pad_o_(mc_cke_o),.mc_cs_pad_o_(mc_cs_o),.mc_sts_pad_i(1'b0),.mc_rp_pad_o_(),.mc_vpen_pad_o(),.mc_adsc_pad_o_(mc_adsc_o),.mc_adv_pad_o_(mc_adv_o),.mc_zz_pad_o(mc_zz_o),.mc_coe_pad_coe_o(mc_pad_oe) // assign memory controller stb_signal assign wb_mc_stb = wb_adr_o[31]; // generate output buffers for memory controller assign mc_dq = mc_doe_o? mc_dq_o : 32'bz; assign mc_dp = mc_doe_o? mc_dp_o : 4'bz; // hookup ssrams (CHIP SELECT 4) mt58l1my18d ssram0 (.Dq( {par_con[24], par_con[16], mc_dq[31:16] ),.Addr(mc_adr_o[19:0]),.Mode(1'b0), // This input (sometimes called LBO) selects burst order // 1'b0 = linear burst, 1'b1 = interleaved burst.adv_n(mc_adv_o),

Hardware/Software co-design para aplicações de processamento de voz 199 );.Clk(mc_clk),.Adsc_n(mc_adsc_o),.Adsp_n(1'b1),.Bwa_n(mc_dqm_o[3]),.Bwb_n(mc_dqm_o[2]), // or the otherway around.bwe_n(mc_we_o),.gw_n(1'b1), //??.Ce_n(mc_cs_o[4]),.Ce2(1'b1),.Ce2_n(1'b0),.Oe_n(mc_oe_o),.Zz(mc_zz_o) mt58l1my18d ssram1 (.Dq( {par_con[8], par_con[0], mc_dq[15:0] ),.Addr(mc_adr_o[19:0]), );.Mode(1'b0), // This input (sometimes called LBO) selects burst order // 1'b0 = linear burst, 1'b1 = interleaved burst.adv_n(mc_adv_o),.clk(mc_clk),.adsc_n(mc_adsc_o),.adsp_n(1'b1),.bwa_n(mc_dqm_o[1]),.bwb_n(mc_dqm_o[0]), // or the otherway around.bwe_n(mc_we_o),.gw_n(1'b1),.ce_n(mc_cs_o[4]),.ce2(1'b1),.ce2_n(1'b0),.oe_n(mc_oe_o),.zz(mc_zz_o) // hookup sdrams (CHIP SELECT 3) mt48lc16m16a2 sdram0_3(.dq(mc_dq[31:16]),.addr(mc_adr_o[12:0]),.ba(mc_adr_o[14:13]),.clk(mc_clk),.cke(mc_cke_o),.cs_n(mc_cs_o[3]),.ras_n(mc_ras_o),.cas_n(mc_cas_o),.we_n(mc_we_o),.dqm(mc_dqm_o[3:2]) ); mt48lc16m16a2 sdram1_3(.dq(mc_dq[15:0]),.addr(mc_adr_o[12:0]),.ba(mc_adr_o[14:13]),.clk(mc_clk),.cke(mc_cke_o),.cs_n(mc_cs_o[3]),.ras_n(mc_ras_o),.cas_n(mc_cas_o),.we_n(mc_we_o),.dqm(mc_dqm_o[1:0]) ); // hookup sdrams (CHIP SELECT 2 or PARITY) assign pbus_o = sel_pbus? (sel_par? mc_dp : set_par) : mc_dq; assign par_con = {7'bz, pbus_o[3], 7'bz, pbus_o[2], 7'bz, pbus_o[1], 7'bz, pbus_o[0]; assign pbus_i = {par_con[24], par_con[16], par_con[8], par_con[0]; assign par_sdram_cs = sel_pbus? mc_cs_o[3] : mc_cs_o[2]; mt48lc16m16a2 sdram0_2(.dq(par_con[31:16]),.addr(mc_adr_o[12:0]),.ba(mc_adr_o[14:13]),.clk(mc_clk),.cke(mc_cke_o),.cs_n(par_sdram_cs),

Hardware/Software co-design para aplicações de processamento de voz 200 );.Ras_n(mc_ras_o),.Cas_n(mc_cas_o),.We_n(mc_we_o),.Dqm(mc_dqm_o[3:2]) mt48lc16m16a2 sdram1_2(.dq(par_con[15:0]),.addr(mc_adr_o[12:0]),.ba(mc_adr_o[14:13]),.clk(mc_clk),.cke(mc_cke_o),.cs_n(par_sdram_cs),.ras_n(mc_ras_o),.cas_n(mc_cas_o),.we_n(mc_we_o),.dqm(mc_dqm_o[1:0]) ); // hookup asynchronous srams (CHIP SELECT 1) A8Kx8 asram0 (.Address(mc_adr_o[12:0]),.dataIO(mc_dq[31:24]),.OEn(mc_oe_o),.CE1n(mc_cs_o[1]),.CE2(1'b1),.WEn(mc_we_o) ); A8Kx8 asram1 (.Address(mc_adr_o[12:0]),.dataIO(mc_dq[23:16]),.OEn(mc_oe_o),.CE1n(mc_cs_o[1]),.CE2(1'b1),.WEn(mc_we_o) ); A8Kx8 asram2 (.Address(mc_adr_o[12:0]),.dataIO(mc_dq[15: 8]),.OEn(mc_oe_o),.CE1n(mc_cs_o[1]),.CE2(1'b1),.WEn(mc_we_o) ); A8Kx8 asram3 (.Address(mc_adr_o[12:0]),.dataIO(mc_dq[ 7: 0]),.OEn(mc_oe_o),.CE1n(mc_cs_o[1]),.CE2(1'b1),.WEn(mc_we_o) ); /*.wbm1_dat_i(wbm1_dat_o),.wbm1_ack_i(wbm1_ack_o),.wbm1_err_i(wbm1_err_o),.wbm1_rty_i(wbm1_rty_o),.wbm1_dat_o(1'b0),.wbm1_we_o(wbm1_we_i),.wbm1_sel_o(wbm1_sel_i),.wbm1_adr_o(wbm1_adr_i),.wbm1_cyc_o(wbm1_cyc_i),.wbm1_stb_o(wbm1_stb_i), */ // hookup wishbone master wb_master_model wbm(.clk(wb_clk),.rst(wb_rst),.adr(wbm1_adr_i),.din(wbm1_dat_o),.dout(wbm1_dat_i),.cyc(wbm1_cyc_i),.stb(wbm1_stb_i),

Hardware/Software co-design para aplicações de processamento de voz 201 );.we(wbm1_we_i),.sel(wbm1_sel_i),.ack(wbm1_ack_o),.err(wbm1_err_o),.rty(1'b0) /* wire [31:0] wbm1_dat_o wire wbm1_ack_o ; wire wbm1_err_o ; wire wbm1_rty_o ; wire [31:0] wbm1_dat_i ; wire wbm1_we_i ; wire [3:0] wbm1_sel_i ; wire [31:0] wbm1_adr_i ; wire wbm1_cyc_i ; wire wbm1_stb_i ; */ /* --------------------------------------- master 2 -----------------------*/ wb_master_model wbm2(.clk(wb_clk),.rst(wb_rst),.adr(wbm2_adr_i),.din(wbm2_dat_o),.dout(wbm2_dat_i),.cyc(wbm2_cyc_i),.stb(wbm2_stb_i),.we(wbm2_we_i),.sel(wbm2_sel_i),.ack(wbm2_ack_o),.err(wbm2_err_o),.rty(1'b0) ); // // testbench body // assign wb_rty_i = 1'b0; // no retries from memory controller // generate clock always #2.5 wb_clk <= ~wb_clk; always@(posedge wb_clk) // mc_clk <= #1 ~mc_clk; mc_clk <= #0 ~mc_clk; // initial statements // testbench original!!!!! initial begin wb_clk = 0; // start with low-level clock wb_rst = 1; // assert reset mc_clk = 0;

Hardware/Software co-design para aplicações de processamento de voz 202 sel_par = 1; // do not modify parity bits sel_pbus = 1; // use second SDRAMS set as parity sdrams repeat(20) @(posedge wb_clk); wb_rst = 0; // negate reset #400 @(posedge wb_clk); //run_tests; a=0; while (1 && a < 2000) begin $display (" Escrita do master 1 \n"); wbm.wb_write(20, 10, 32'h6000_0008, `BA_MASK); // program base address register $display (" leitura e comparação do master 2\n"); wbm2.wb_cmp(0, 0, 32'h6000_0008, `BA_MASK); $display (" Escrita do master 2 \n"); wbm2.wb_write(0, 0, 32'h6000_0000, 32'h6000_0400); // program CSR // check written data $display (" leitura e comparação do master 1\n"); wbm.wb_cmp(0, 0, 32'h6000_0000, 32'h6000_0400); a=a+1; end // show total errors detected wbm.show_tot_err_cnt; wbm2.show_tot_err_cnt; end $stop; ////////////////////// // // Internal tasks // task run_tests; begin prg_mc; // program memory controller BA-mask and CSR registers // force sdram0_3.debug = 1'b1; // turn on SDRAM debug option force sdram0_3.debug = 1'b0; // turn off SDRAM debug option /////////////// // SDRAM tests // tst_sdram_memfill; // test sdrams: Fill entire memory and verify // tst_sdram_parity; // test sdrams: Parity generation // tst_sdram_seq; // test sdrams: Fill-Verify, sequential access // tst_sdram_rnd; // test sdrams: Fill-Verify, random access // tst_sdram_rmw_seq; // test sdrams: Read-Modify-Write test, sequential access // tst_sdram_rmw_rnd; // test sdrams: Read-Modify-Write test, random access // tst_sdram_blk_cpy1; // test sdrams: Perform block copy, different src and dest. address // tst_sdram_blk_cpy2; // test sdrams: Perform block copy, src and dest same address // tst_sdram_bytes; // test sdrams: Peform byte accesses ////////////////////////////// // ASYNCHRONOUS MEMORIES TEST // tst_amem_seq; // test asynchronous memory // tst_amem_b2b; // test asynchronous memory back-2-back //////////////// // SSRAMS TESTS tst_ssram_seq; ////////////////////// // MULTI MEMORY TESTS // tst_blk_cpy1; // test block-copy: access sdrams + asrams

Hardware/Software co-design para aplicações de processamento de voz 203 // The next test (tst_blk_cyp2) is, saddly to say, useless. // It tests n-by-n situations for multiple SDRAMS, testing all possible settings for each SDRAM. // It is supposed to test the independence for each SDRAM chipselect. // However it is to time-consuming; it runs for about a month on an Athlon-XP 1800 system // tst_blk_cpy2; // test block-copy: access multiple sdrams ///////////////////////////// // EXTERNAL BUS MASTER TESTS // turn on external bus-master and rerun some tests // force ext_bm.on_off = 1'b1; // tst_sdram_seq; // test sdrams: Fill-Verify, sequential access // tst_amem_seq; // test asynchronous memory // tst_amem_b2b; // test asynchronous memory back-2-back // tst_blk_cpy1; // test block-copy: access sdrams + asrams end endtask // run_tests task prg_mc; begin wbm.wb_write(20, 10, 32'h6000_0008, `BA_MASK); // program base address register wbm.wb_write(0, 0, 32'h6000_0000, 32'h6000_0400); // program CSR a=1; // check written data wbm.wb_cmp(0, 0, 32'h6000_0008, `BA_MASK); wbm.wb_cmp(0, 0, 32'h6000_0000, 32'h6000_0400); end endtask //prg_mc //////////////////////////////// // Register test // task reg_test; begin end endtask // reg_test ///////////////////////// // include memory tests // // `include "tst_sdram.v" // `include "tst_asram.v" `include "tst_ssram.v" // `include "tst_multi_mem.v" endmodule

Hardware/Software co-design para aplicações de processamento de voz 204 Anexo 14: bench2.v ///////////////////////////////////////////////////////////////////// //// //// //// OpenCores Memory Controller Testbench //// //// Main testbench //// //// //// //// Author: Richard Herveille //// //// richard@asics.ws //// //// //// //// //// //// Downloaded from: http://www.opencores.org/cores/mem_ctrl/ //// //// //// ///////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2001 Richard Herveille //// //// richard@asics.ws //// //// //// //// This source file may be used and distributed without //// //// restriction provided that this copyright statement is not //// //// removed from the file and that any derivative work contains //// //// the original copyright notice and the associated disclaimer.//// //// //// //// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// //// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// //// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// //// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// //// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// //// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// //// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// //// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// //// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// //// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// //// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// //// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// //// POSSIBILITY OF SUCH DAMAGE. //// //// //// ///////////////////////////////////////////////////////////////////// // ToDo: // 1) add power-on configuration // 2) test SSRAM // 3) test synchronous devices??? // // CVS Log // // $Id: bench.v,v 1.1 2002/03/06 15:10:34 rherveille Exp $ // // $Date: 2002/03/06 15:10:34 $ // $Revision: 1.1 $ // $Author: rherveille $ // $Locker: $ // $State: Exp $ // // Change History: // $Log: bench.v,v $ // Revision 1.1 2002/03/06 15:10:34 rherveille // Initial release // // `include "timescale.v" `define SDRAM_ROWA_HI 12 // row address hi-bit `define SDRAM_COLA_HI 8 // column address hi-bit `define BA_MASK 32'h0000_00e0 // base address mask `define SDRAM1_LOC 32'h0400_0000 // location of sdram1 in address-space `define SDRAM2_LOC 32'h0800_0000 // location of sdram2 in address-space `define SRAM_LOC 32'h0C00_0000 // location of srams in address-space `define SSRAM_LOC 32'h1000_0000 // location of ssrams in address-space

Hardware/Software co-design para aplicações de processamento de voz 205 module SSRAM_PREENCHE_bench_top(); // // internal wires // reg wb_clk; reg mc_clk; reg [ 31:0] data; integer data1; reg wb_rst; wire [31:0] wb_dat_i, wb_dat_o; wire [31:0] wb_adr_o; wire wb_cyc_o, wb_stb_o; wire [ 3:0] wb_sel_o; wire wb_ack_i, wb_err_i, wb_rty_i; wire wb_mc_stb; wire [23:0] mc_adr_o; wire [31:0] mc_dq, mc_dq_o; wire [ 3:0] mc_dp, mc_dp_o, pbus_o, pbus_i; reg [ 3:0] set_par; wire [31:0] par_con; reg sel_par, sel_pbus; wire par_sdram_cs; wire mc_doe_o; wire [ 3:0] mc_dqm_o; wire mc_we_o, mc_oe_o; wire mc_ras_o, mc_cas_o, mc_cke_o; wire [ 7:0] mc_cs_o; wire mc_pad_oe; wire mc_adsc_o, mc_adv_o, mc_zz_o; // ssram connections wire ext_br, ext_bg; integer flag = 0; /******************* wires needed to connect master1 to master/slave block*******/ wire [31:0] wbm1_dat_o; wire wbm1_ack_o ; wire wbm1_err_o ; wire wbm1_rty_o ; wire [31:0] wbm1_dat_i ; wire wbm1_we_i ; wire [3:0] wbm1_sel_i ; wire [31:0] wbm1_adr_i ; wire wbm1_cyc_i ; wire wbm1_stb_i ; /******************************************************************************/ /******************* wires needed to connect master2 to master/slave block*******/ wire [31:0] wbm2_dat_o; wire wbm2_ack_o ; wire wbm2_err_o ; wire wbm2_rty_o ; wire [31:0] wbm2_dat_i ; wire wbm2_we_i ; wire [3:0] wbm2_sel_i ; wire [31:0] wbm2_adr_i ; wire wbm2_cyc_i ; wire wbm2_stb_i ; real a ; /******************************************************************************/

Hardware/Software co-design para aplicações de processamento de voz 206 // // hookup modules // // hookup watch-dog counter /*watch_dog #(1024) wdog (.clk(wb_clk),.cyc_i(wbm1_cyc_i),.ack_i(wbm1_ack_o),.adr_i(wbm1_adr_i) ); watch_dog #(1024) wdog2 (.clk(wb_clk),.cyc_i(wbm2_cyc_i),.ack_i(wbm2_ack_o),.adr_i(wbm2_adr_i) );*/ // hookup external bus-master model /*bm_model ext_bm(.br(ext_br),.bg(ext_bg),.chk(mc_pad_oe) );*/ // hookup ERR checker err_check err_chk(wb_err_o, sel_par); // hookup CSn checker cs_check cs_chec(mc_cs_o); intercon master_slave( /* -- wishbone master port(s) -- wbm1*/.wbm1_dat_i(wbm1_dat_o),.wbm1_ack_i(wbm1_ack_o),.wbm1_err_i(wbm1_err_o), //.wbm1_rty_i(),.wbm1_dat_o(wbm1_dat_i),.wbm1_we_o(wbm1_we_i),.wbm1_sel_o(wbm1_sel_i),.wbm1_adr_o(wbm1_adr_i),.wbm1_cyc_o(wbm1_cyc_i),.wbm1_stb_o(wbm1_stb_i), /* -- wbm2 */.wbm2_dat_i(wbm2_dat_o),.wbm2_ack_i(wbm2_ack_o),.wbm2_err_i(wbm2_err_o), //.wbm2_rty_i(),.wbm2_dat_o(wbm2_dat_i),.wbm2_we_o(wbm2_we_i),.wbm2_sel_o(wbm2_sel_i),.wbm2_adr_o(wbm2_adr_i),.wbm2_cyc_o(wbm2_cyc_i),.wbm2_stb_o(wbm2_stb_i), /* wishbone slave port(s)

Hardware/Software co-design para aplicações de processamento de voz 207 wbs1 */.wbs1_dat_o(wb_dat_i),.wbs1_ack_o(wb_ack_i),.wbs1_err_o(wb_err_i),.wbs1_rty_o(1'b0),.wbs1_dat_i(wb_dat_o),.wbs1_we_i (wb_we_o),.wbs1_sel_i(wb_sel_o),.wbs1_adr_i(wb_adr_o),.wbs1_cyc_i(wb_cyc_o),.wbs1_stb_i(wb_stb_o),.clk (wb_clk),.reset (wb_rst ) ); // hookup memory controller mc_top dut ( // wishbone interface.clk_i(wb_clk),.rst_i(wb_rst),.wb_data_i(wb_dat_o),.wb_data_o(wb_dat_i),.wb_addr_i(wb_adr_o),.wb_sel_i(wb_sel_o),.wb_we_i(wb_we_o),.wb_cyc_i(wb_cyc_o),.wb_stb_i(wb_stb_o),.wb_ack_o(wb_ack_i),.wb_err_o(wb_err_i), ); // memory controller.susp_req_i(1'b0),.resume_req_i(1'b0),.suspended_o(),.poc_o(),.mc_clk_i(mc_clk),.mc_br_pad_i(ext_br),.mc_bg_pad_o(ext_bg),.mc_ack_pad_i(1'b0),.mc_addr_pad_o(mc_adr_o),.mc_data_pad_i(mc_dq),.mc_data_pad_o(mc_dq_o),.mc_dp_pad_i(pbus_i), // attach parity bus.mc_dp_pad_o(mc_dp_o),.mc_doe_pad_doe_o(mc_doe_o),.mc_dqm_pad_o(mc_dqm_o),.mc_oe_pad_o_(mc_oe_o),.mc_we_pad_o_(mc_we_o),.mc_cas_pad_o_(mc_cas_o),.mc_ras_pad_o_(mc_ras_o),.mc_cke_pad_o_(mc_cke_o),.mc_cs_pad_o_(mc_cs_o),.mc_sts_pad_i(1'b0),.mc_rp_pad_o_(),.mc_vpen_pad_o(),.mc_adsc_pad_o_(mc_adsc_o),.mc_adv_pad_o_(mc_adv_o),.mc_zz_pad_o(mc_zz_o),.mc_coe_pad_coe_o(mc_pad_oe) // assign memory controller stb_signal assign wb_mc_stb = wb_adr_o[31]; // generate output buffers for memory controller assign mc_dq = mc_doe_o? mc_dq_o : 32'bz; assign mc_dp = mc_doe_o? mc_dp_o : 4'bz; // hookup ssrams (CHIP SELECT 4) CY7C1340G_PLDCD cypress

Hardware/Software co-design para aplicações de processamento de voz 208 (.ZZ(mc_zz_o),.Mode(1'b0),.ADDR(mc_adr_o[16:0]),.GW_N(1'b1),.BWE_N(mc_we_o),.BWd_N(mc_dqm_o[3]),.BWc_N(mc_dqm_o[2]),.BWb_N(mc_dqm_o[1]),.BWa_N(mc_dqm_o[0]),.CE1_N(mc_cs_o[4]),.CE2(1'b1),.CE3_N(1'b0),.ADSP_N(1'b1),.ADSC_N(mc_adsc_o),.ADV_N(mc_adv_o),.OE_N(mc_oe_o),.DQ (mc_dq),.clk (mc_clk) ); /*mt58l1my18d ssram0 (.Dq( {par_con[24], par_con[16], mc_dq[31:16] ),.Addr(mc_adr_o[19:0]), );.Mode(1'b0), // This input (sometimes called LBO) selects burst order // 1'b0 = linear burst, 1'b1 = interleaved burst.adv_n(mc_adv_o),.clk(mc_clk),.adsc_n(mc_adsc_o),.adsp_n(1'b1),.bwa_n(mc_dqm_o[3]),.bwb_n(mc_dqm_o[2]), // or the otherway around.bwe_n(mc_we_o),.gw_n(1'b1), //??.Ce_n(mc_cs_o[4]),.Ce2(1'b1),.Ce2_n(1'b0),.Oe_n(mc_oe_o),.Zz(mc_zz_o) mt58l1my18d ssram1 (.Dq( {par_con[8], par_con[0], mc_dq[15:0] ),.Addr(mc_adr_o[19:0]), );*/.Mode(1'b0), // This input (sometimes called LBO) selects burst order // 1'b0 = linear burst, 1'b1 = interleaved burst.adv_n(mc_adv_o),.clk(mc_clk),.adsc_n(mc_adsc_o),.adsp_n(1'b1),.bwa_n(mc_dqm_o[1]),.bwb_n(mc_dqm_o[0]), // or the otherway around.bwe_n(mc_we_o),.gw_n(1'b1),.ce_n(mc_cs_o[4]),.ce2(1'b1),.ce2_n(1'b0),.oe_n(mc_oe_o),.zz(mc_zz_o) // hookup sdrams (CHIP SELECT 3) mt48lc16m16a2 sdram0_3(.dq(mc_dq[31:16]),.addr(mc_adr_o[12:0]),.ba(mc_adr_o[14:13]),.clk(mc_clk),.cke(mc_cke_o),.cs_n(mc_cs_o[3]),.ras_n(mc_ras_o),.cas_n(mc_cas_o),.we_n(mc_we_o),.dqm(mc_dqm_o[3:2]) );

Hardware/Software co-design para aplicações de processamento de voz 209 mt48lc16m16a2 sdram1_3(.dq(mc_dq[15:0]),.addr(mc_adr_o[12:0]),.ba(mc_adr_o[14:13]),.clk(mc_clk),.cke(mc_cke_o),.cs_n(mc_cs_o[3]),.ras_n(mc_ras_o),.cas_n(mc_cas_o),.we_n(mc_we_o),.dqm(mc_dqm_o[1:0]) ); // hookup sdrams (CHIP SELECT 2 or PARITY) assign pbus_o = sel_pbus? (sel_par? mc_dp : set_par) : mc_dq; assign par_con = {7'bz, pbus_o[3], 7'bz, pbus_o[2], 7'bz, pbus_o[1], 7'bz, pbus_o[0]; assign pbus_i = {par_con[24], par_con[16], par_con[8], par_con[0]; assign par_sdram_cs = sel_pbus? mc_cs_o[3] : mc_cs_o[2]; mt48lc16m16a2 sdram0_2(.dq(par_con[31:16]),.addr(mc_adr_o[12:0]),.ba(mc_adr_o[14:13]),.clk(mc_clk),.cke(mc_cke_o),.cs_n(par_sdram_cs),.ras_n(mc_ras_o),.cas_n(mc_cas_o),.we_n(mc_we_o),.dqm(mc_dqm_o[3:2]) ); mt48lc16m16a2 sdram1_2(.dq(par_con[15:0]),.addr(mc_adr_o[12:0]),.ba(mc_adr_o[14:13]),.clk(mc_clk),.cke(mc_cke_o),.cs_n(par_sdram_cs),.ras_n(mc_ras_o),.cas_n(mc_cas_o),.we_n(mc_we_o),.dqm(mc_dqm_o[1:0]) ); // hookup asynchronous srams (CHIP SELECT 1) A8Kx8 asram0 (.Address(mc_adr_o[12:0]),.dataIO(mc_dq[31:24]),.OEn(mc_oe_o),.CE1n(mc_cs_o[1]),.CE2(1'b1),.WEn(mc_we_o) ); A8Kx8 asram1 (.Address(mc_adr_o[12:0]),.dataIO(mc_dq[23:16]),.OEn(mc_oe_o),.CE1n(mc_cs_o[1]),.CE2(1'b1),.WEn(mc_we_o) ); A8Kx8 asram2 (.Address(mc_adr_o[12:0]),.dataIO(mc_dq[15: 8]),.OEn(mc_oe_o),.CE1n(mc_cs_o[1]),.CE2(1'b1),.WEn(mc_we_o) ); A8Kx8 asram3 (

Hardware/Software co-design para aplicações de processamento de voz 210 );.Address(mc_adr_o[12:0]),.dataIO(mc_dq[ 7: 0]),.OEn(mc_oe_o),.CE1n(mc_cs_o[1]),.CE2(1'b1),.WEn(mc_we_o) /*.wbm1_dat_i(wbm1_dat_o),.wbm1_ack_i(wbm1_ack_o),.wbm1_err_i(wbm1_err_o),.wbm1_rty_i(wbm1_rty_o),.wbm1_dat_o(1'b0),.wbm1_we_o(wbm1_we_i),.wbm1_sel_o(wbm1_sel_i),.wbm1_adr_o(wbm1_adr_i),.wbm1_cyc_o(wbm1_cyc_i),.wbm1_stb_o(wbm1_stb_i), */ // hookup wishbone master wb_master_model wbm(.clk(wb_clk),.rst(wb_rst),.adr(wbm1_adr_i),.din(wbm1_dat_o),.dout(wbm1_dat_i),.cyc(wbm1_cyc_i),.stb(wbm1_stb_i),.we(wbm1_we_i),.sel(wbm1_sel_i),.ack(wbm1_ack_o),.err(wbm1_err_o),.rty(1'b0) ); /* wire [31:0] wbm1_dat_o wire wbm1_ack_o ; wire wbm1_err_o ; wire wbm1_rty_o ; wire [31:0] wbm1_dat_i ; wire wbm1_we_i ; wire [3:0] wbm1_sel_i ; wire [31:0] wbm1_adr_i ; wire wbm1_cyc_i ; wire wbm1_stb_i ; */ /* --------------------------------------- master 2 -----------------------*/ wb_master_model wbm2(.clk(wb_clk),.rst(wb_rst),.adr(wbm2_adr_i),.din(wbm2_dat_o),.dout(wbm2_dat_i),.cyc(wbm2_cyc_i),.stb(wbm2_stb_i),.we(wbm2_we_i),.sel(wbm2_sel_i),.ack(wbm2_ack_o),.err(wbm2_err_o),.rty(1'b0) ); // // testbench body //

Hardware/Software co-design para aplicações de processamento de voz 211 assign wb_rty_i = 1'b0; // no retries from memory controller // generate clock always #20 wb_clk <= ~wb_clk; always@(posedge wb_clk) // mc_clk <= #1 ~mc_clk; mc_clk <= #0 ~mc_clk; // initial statements // testbench original!!!!! /*initial begin a=0; # 100000; $display (" operando com o master 2\n"); //teste_m2; while (a<2000) begin address register end */ end $display (" Escrita do master 2 \n"); wbm2.wb_write(0, 0, 32'h1000_0000, 32'hff_dd_ee_cc); // program base $display (" leitura e comparação do master 2\n"); wbm2.wb_read(0, 0, 32'h1000_0fff, data); /* $display (" Escrita do master 2 \n"); wbm2.wb_write(0, 0, 32'h6000_0000, 32'h6000_0400); // program CSR // check written data $display (" leitura e comparação do master 2\n"); wbm2.wb_cmp(0, 0, 32'h6000_0000, 32'h6000_0400); a=a+1; //if (a==1) flag=1; $display (" o valor do data é %h\n",data); initial begin wb_clk = 0; // start with low-level clock wb_rst = 1; // assert reset mc_clk = 0; sel_par = 1; // do not modify parity bits sel_pbus = 1; // use second SDRAMS set as parity sdrams repeat(20) @(posedge wb_clk); wb_rst = 0; // negate reset @(posedge wb_clk); run_tests; #30000; // show total errors detected wbm.show_tot_err_cnt; wbm2.show_tot_err_cnt; end $stop; //////////////////////

Hardware/Software co-design para aplicações de processamento de voz 212 // // Internal tasks // task run_tests; begin prg_mc; // program memory controller BA-mask and CSR registers // force sdram0_3.debug = 1'b1; // turn on SDRAM debug option force sdram0_3.debug = 1'b0; // turn off SDRAM debug option /////////////// // SDRAM tests // tst_sdram_memfill; // test sdrams: Fill entire memory and verify // tst_sdram_parity; // test sdrams: Parity generation // tst_sdram_seq; // test sdrams: Fill-Verify, sequential access // tst_sdram_rnd; // test sdrams: Fill-Verify, random access // tst_sdram_rmw_seq; // test sdrams: Read-Modify-Write test, sequential access // tst_sdram_rmw_rnd; // test sdrams: Read-Modify-Write test, random access // tst_sdram_blk_cpy1; // test sdrams: Perform block copy, different src and dest. address // tst_sdram_blk_cpy2; // test sdrams: Perform block copy, src and dest same address // tst_sdram_bytes; // test sdrams: Peform byte accesses ////////////////////////////// // ASYNCHRONOUS MEMORIES TEST // tst_amem_seq; // test asynchronous memory // tst_amem_b2b; // test asynchronous memory back-2-back //////////////// // SSRAMS TESTS //teste_m1; // tst_ssram_seq; fill; ////////////////////// // MULTI MEMORY TESTS // tst_blk_cpy1; // test block-copy: access sdrams + asrams // The next test (tst_blk_cyp2) is, saddly to say, useless. // It tests n-by-n situations for multiple SDRAMS, testing all possible settings for each SDRAM. // It is supposed to test the independence for each SDRAM chipselect. // However it is to time-consuming; it runs for about a month on an Athlon-XP 1800 system // tst_blk_cpy2; // test block-copy: access multiple sdrams ///////////////////////////// // EXTERNAL BUS MASTER TESTS // turn on external bus-master and rerun some tests // force ext_bm.on_off = 1'b1; // tst_sdram_seq; // test sdrams: Fill-Verify, sequential access // tst_amem_seq; // test asynchronous memory // tst_amem_b2b; // test asynchronous memory back-2-back // tst_blk_cpy1; // test block-copy: access sdrams + asrams end endtask // run_tests register task prg_mc; begin wbm.wb_write(0, 0, 32'h6000_0008, `BA_MASK); // program base address wbm.wb_write(0, 0, 32'h6000_0000, 32'h6000_0400); // program CSR

Hardware/Software co-design para aplicações de processamento de voz 213 // check written data $display(" teste da configuração\n"); wbm.wb_cmp(0, 0, 32'h6000_0008, `BA_MASK); wbm.wb_cmp(0, 0, 32'h6000_0000, 32'h6000_0400); end endtask //prg_mc //////////////////////////////// // Register test // task reg_test; begin end endtask // reg_test ///////////////////////// // include memory tests // // `include "tst_sdram.v" // `include "tst_asram.v" `include "tst_ssram_1.v" `include "tst_ssram_2.v" `include "tst_ssram.v" // `include "tst_multi_mem.v" `include "func.v" Endmodule

Hardware/Software co-design para aplicações de processamento de voz 214 Anexo 15: Or1k TOP level

Hardware/Software co-design para aplicações de processamento de voz 215 Anexo 16: mc_defines.v ///////////////////////////////////////////////////////////////////// //// //// //// WISHBONE Memory Controller Definitions //// //// //// //// //// //// Author: Rudolf Usselmann //// //// rudi@asics.ws //// //// //// //// //// //// Downloaded from: http://www.opencores.org/cores/mem_ctrl/ //// //// //// ///////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2000-2002 Rudolf Usselmann //// //// www.asics.ws //// //// rudi@asics.ws //// //// //// //// This source file may be used and distributed without //// //// restriction provided that this copyright statement is not //// //// removed from the file and that any derivative work contains //// //// the original copyright notice and the associated disclaimer.//// //// //// //// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// //// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// //// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// //// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// //// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// //// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// //// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// //// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// //// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// //// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// //// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// //// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// //// POSSIBILITY OF SUCH DAMAGE. //// //// //// ///////////////////////////////////////////////////////////////////// // CVS Log // // $Id: mc_defines.v,v 1.1 2002/03/06 15:10:34 rherveille Exp $ // // $Date: 2002/03/06 15:10:34 $ // $Revision: 1.1 $ // $Author: rherveille $ // $Locker: $ // $State: Exp $ // // Change History: // $Log: mc_defines.v,v $ // Revision 1.1 2002/03/06 15:10:34 rherveille // Initial release // // Revision 1.6 2001/12/12 06:35:15 rudi // *** empty log message *** // // Revision 1.5 2001/12/11 02:47:19 rudi // // - Made some changes not to expect clock during reset... // // Revision 1.4 2001/11/29 02:16:28 rudi // // // - More Synthesis cleanup, mostly for speed // - Several bug fixes // - Changed code to avoid auto-precharge and // burst-terminate combinations (apparently illegal?) // Now we will do a manual precharge... // // Revision 1.3 2001/09/10 13:44:17 rudi // *** empty log message *** // // Revision 1.2 2001/08/10 08:16:21 rudi //

Hardware/Software co-design para aplicações de processamento de voz 216 // - Changed IO names to be more clear. // - Uniquifyed define names to be core specific. // - Removed "Refresh Early" configuration // // Revision 1.1 2001/07/29 07:34:41 rudi // // // 1) Changed Directory Structure // 2) Fixed several minor bugs // // Revision 1.3 2001/06/12 15:19:49 rudi // // // Minor changes after running lint, and a small bug // fix reading csr and ba_mask registers. // // Revision 1.2 2001/06/03 11:37:17 rudi // // // 1) Fixed Chip Select Mask Register // - Power On Value is now all ones // - Comparison Logic is now correct // // 2) All resets are now asynchronous // // 3) Converted Power On Delay to an configurable item // // 4) Added reset to Chip Select Output Registers // // 5) Forcing all outputs to Hi-Z state during reset // // Revision 1.1.1.1 2001/05/13 09:39:38 rudi // Created Directory Structure // // // // `timescale 1ns / 10ps ///////////////////////////////////////////////////////////////////// // // This define selects how the WISHBONE interface determines if // the internal register file is selected. // This should be a simple address decoder. "wb_addr_i" is the // WISHBONE address bus (32 bits wide). `define MC_REG_SEL (wb_addr_i[31:29] == 3'b011) // This define selects how the WISHBONE interface determines if // the memory is selected. // This should be a simple address decoder. "wb_addr_i" is the // WISHBONE address bus (32 bits wide). `define MC_MEM_SEL (wb_addr_i[31:29] == 3'h0) ///////////////////////////////////////////////////////////////////// // // This are the default Power-On Reset values for Chip Select // // This will be defined by the run script for my test bench... // Alternatively force here for synthesis... `define RUDIS_TB 1 // Defines which chip select is used for Power On booting // To run my default testbench default boot CS must be 3!!! `ifdef RUDIS_TB `define MC_DEF_SEL 3'h3 `else `define MC_DEF_SEL 3'h0 `endif // Defines the default (reset) TMS value for the DEF_SEL chip select `define MC_DEF_POR_TMS 32'hffff_ffff /////////////////////////////////////////////////////////////////////

Hardware/Software co-design para aplicações de processamento de voz 217 // // Define how many Chip Selects to Implement // `define MC_HAVE_CS1 1 //`define MC_HAVE_CS2 1 //`define MC_HAVE_CS3 1 //`define MC_HAVE_CS4 1 //`define MC_HAVE_CS5 1 //`define MC_HAVE_CS6 1 //`define MC_HAVE_CS7 1 // To run my default testbench those need to there!!! `ifdef RUDIS_TB `define MC_HAVE_CS2 1 `define MC_HAVE_CS3 1 `define MC_HAVE_CS4 1 `define MC_HAVE_CS5 1 `endif ///////////////////////////////////////////////////////////////////// // // Init Refresh // // Number of Refresh Cycles to perform during SDRAM initialization. // This varies between SDRAM manufacturer. Typically this value is // between 2 and 8. This number must be smaller than 16. `define MC_INIT_RFRC_CNT 2 ///////////////////////////////////////////////////////////////////// // // Power On Delay // // Most if SDRAMs require some time to initialize before they can be used // after power on. If the Memory Controller shall stall after power on to // allow SDRAMs to finish the initialization process uncomment the below // define statement `define MC_POR_DELAY 1 // This value defines how many MEM_CLK cycles the Memory Controller should // stall. Default is 2.5uS. At a 10nS MEM_CLK cycle time, this would 250 // cycles. `define MC_POR_DELAY_VAL 8'd250 // =============================================================== // =============================================================== // Various internal defines (DO NOT MODIFY!) // =============================================================== // =============================================================== // Register settings encodings `define MC_BW_8 `define MC_BW_16 2'h1 `define MC_BW_32 2'h2 2'h0 `define MC_MEM_TYPE_SDRAM `define MC_MEM_TYPE_SRAM `define MC_MEM_TYPE_ACS `define MC_MEM_TYPE_SCS `define MC_MEM_SIZE_64 `define MC_MEM_SIZE_128 `define MC_MEM_SIZE_256 3'h0 3'h1 2'h0 3'h2 3'h3 2'h1 2'h2 // Command Valid, Ras_, Cas_, We_ `define MC_CMD_NOP 4'b0111 `define MC_CMD_PC 4'b1010 `define MC_CMD_ACT 4'b1011 `define MC_CMD_WR 4'b1100 `define MC_CMD_RD 4'b1101 `define MC_CMD_BT 4'b1110 `define MC_CMD_ARFR 4'b1001 `define MC_CMD_LMR 4'b1000 `define MC_CMD_XRD 4'b1111 `define MC_CMD_XWR 4'b1110 `define MC_SINGLE_BANK 1'b0

Hardware/Software co-design para aplicações de processamento de voz 218 `define MC_ALL_BANKS 1'b1

Hardware/Software co-design para aplicações de processamento de voz 219 Anexo 17 : Top level do master slave