hardware/software co-design para aplicações de...
TRANSCRIPT
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 -
Pedro Mota | Pedro Santos
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 -
Pedro Mota | Pedro Santos
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 -
Pedro Mota | Pedro Santos
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 -
Pedro Mota | Pedro Santos
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 -
Pedro Mota | Pedro Santos
5
Hardware/Software co-design para aplicações de processamento de voz -
Pedro Mota | Pedro Santos
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 -
Pedro Mota | Pedro Santos
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 -
Pedro Mota | Pedro Santos
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 -
Pedro Mota | Pedro Santos
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 -
Pedro Mota | Pedro Santos
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 -
Pedro Mota | Pedro Santos
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 -
Pedro Mota | Pedro Santos
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 -
Pedro Mota | Pedro Santos
13
8bit / A-lawto
13-bituniform
LPF A/D
1
2
MS side only
BSS side only GSM 06.60.AMR
GSM 03.50
TRANSMIT SIDE
SpeechEncoder
ComfortNoise
TXFunctions
VoiceActivity
Detector
DTXControl
andOperation
3
6
4
5
6
7
GSM 06.82.AMR GSM 06.81.AMR
GSM 06.60.AMR
GSM 06.62.AMR
SID frame
Speech frame
VAD
13-bituniform
to8bit / A-law
LPFD/A
1
8
MS side only
BSS side only GSM 06.60.AMR
GSM 03.50
RECEIVE SIDE
SpeechDecoder
Speechframe
substitution
DTXControl
andOperation
4
5
9
10
GSM 06.61.AMRGSM 06.81.AMR
GSM 06.60.AMR
GSM 06.62.AMR
SID frame
Speech frame
ComfortNoise
RXFunctions
11
2
SPflag
Info.bits
BFI
Info.bits
SID
TAF
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 -
Pedro Mota | Pedro Santos
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:
( )( )H z
A z a zii
im= =
+ −=∑
1 1
1 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
1B z g zpT=
− −
onde T é o atraso do pitch e gp o seu ganho. Este filtro é implementado usando técnicas
adaptativas.
O modelo de síntese deste codec de voz é ilustrado na figura seguinte:
A(z)1 s(n)^
+
v(n)
c(n)
u(n)
gc
fixedcodebook
adaptive codebook gp
LP synthesis
post-filtering s'(n)^
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 -
Pedro Mota | Pedro Santos
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:
( )( )( )W z
A z
A z=
γ
γ1
2
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
Hardware/Software co-design para aplicações de processamento de voz -
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
win
dow
ing
and
auto
corr
elat
ion
R[ ]
Levi
nson
-D
urbi
nR
[ ]A(
z)
A(z) LS
Pqu
antiz
atio
n
com
pute
targ
etfo
rin
nova
tion
upda
te fi
lter
mem
orie
s for
next
sub
fram
e
Ope
n-lo
op p
itch
sear
chAd
aptiv
e co
debo
okse
arch
Inno
vativ
e co
debo
okse
arch
Filte
r mem
ory
upda
te
inte
rpol
atio
n
subf
ram
esLS
PA(
z)
LSP
com
pute
wei
ghte
dsp
eech
(4 s
ubfr
ames
)
find
open
-loop
pitc
h
find
best
in
nova
tion
fixed
co
debo
okga
in
quan
tizat
ion
A(z)
^
x(n)
pitc
hin
dex
code
inde
x
fram
esu
bfra
me
s(n)
com
pute
targ
etfo
r ada
ptiv
eco
debo
ok
T ofin
d be
st d
elay
and
gain
x(n) com
pute
impu
lse
resp
onse
A(z)
^A(z)
h(n)
h(n)
A(z)
LPC
ana
lysi
s(tw
ice
per f
ram
e)
A(z)
(twic
e pe
r fra
me)
x (n
)2
quan
tize
LTP-
gain
com
pute
adap
tive
code
book
cont
ribut
ion
LSP
indi
ces
LTP
gain
inde
x
gain
inde
xfix
ed c
odeb
ook
inte
rpol
atio
nfo
r the
4su
bfra
mes
LSP
A(z)
^
for t
he 4
Pre-
proc
essi
ng
Pre-
proc
essin
g
com
pute
exci
tatio
n
Hardware/Software co-design para aplicações de processamento de voz 18
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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.
LSPindices
decode LSP
interpolation of LSP for the4 subframes
LSP
decodeadaptivecodebook
decodeinnovativecodebook
pitchindex
codeindex
decodegains
A(z)^
constructexcitation
frame subframe post-processing
s'(n)^s(n)^post filter
gainsindices
synthesisfilter
Hardware/Software co-design para aplicações de processamento de voz 20
Pedro Mota | Pedro Santos
No esquema seguinte pode-se ver um diagrama de blocos representativo deste
algoritmo.
Filter bankandcomputationof sub-bandlevels
VADdecision
Pitchdetection
Tonedetection
T_op[n]
t0,t1
VAD_flag
level[n]
pitch
tone
s(i)
Complexsignalanalysis
OL-LTPcorrelationvector
complex_warningTonedetectiont0,t1
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
Pedro Mota | Pedro Santos
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.
RX_TYPE
Mode Indication
Information bits
TX SCR handler
SpeechEncoder
VoiceActivityDetector
Comfort NoiseParameter
Computation
“Network”
Informationpacketing,
transport andclassification
Information bits
Mode Indication
TX_TYPE
RX SCR handler
SpeechDecoder
ErrorConcealment
ComfortNoise
Generation
Figura 6: Diagrama de blocos de uma ligação com operação SCR.
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
- 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
Pedro Mota | Pedro Santos
Struct name Variable Type[Length] Description Speech_Encode_ FrameState
cod_amr_state cod_amrState see below in this table
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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 Speech_Decode_FrameState
decoder_amrState Decoder_amrState see below in this table
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
e see below in this table
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
Speech_Encode_Frame Pre_Process cod_amr 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_tone_detection Inv_sqrt comp_corr hp_max2 vad_complex_detection_update Pitch_ol_wgh comp_corr Lag_max vad_tone_detection_update vad_tone_detection gmed_n hp_max vad_complex_detection_update vad_pitch_detection subframePreProc Weight_Ai Syn_filt Residu Copy cl_ltp Pitch_fr getRange Norm_Corr Convolve Inv_sqrt searchFrac Interpol_3or6 Enc_lag3 Enc_lag6 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 Prm2bits Int2bin
Tabela 4: Estrutura da chamada das funções do codificador.
Hardware/Software co-design para aplicações de processamento de voz 29
Pedro Mota | Pedro Santos
cbsearch code_2i40_9bits cor_h_x set_sign cor_h Inv_sqrt search_2i40 build_code code_2i40_11bits cor_h_x set_sign cor_h Inv_sqrt search_2i40 build_code code_3i40_14bits cor_h_x set_sign cor_h Inv_sqrt search_3i40 build_code code_4i40_17bits cor_h_x set_sign cor_h Inv_sqrt search_4i40 build_code code_8i40_31bits cor_h_x set_sign12k2 Inv_sqrt cor_h Inv_sqrt search_10and8i40 build_code compress_code compress10 code_10i40_35bits cor_h_x set_sign12k2 Inv_sqrt cor_h 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
Pedro Mota | Pedro Santos
Speech_Decode_Frame Bits2prm Bin2int Decoder_amr rx_dtx_handler Decoder_amr_reset lsp_avg_reset D_plsf_reset ec_gain_pitch_reset ec_gain_code_reset gc_pred_reset Bgn_scd_reset Set_zero ph_disp_reset dtx_dec_reset Copy Set_zero dtx_dec Copy Lsf_lsp Init_D_plsf_3 Copy D_plsf_3 Reorder_lsf Copy Lsf_lsp pseudonoise Lsp_lsf Reorder_lsf Lsp_Az Get_lsp_pol A_Refl Log2 Log2_norm Build_CN_code pseudonoise Syn_filt 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 Post_Filter 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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:[email protected]:/home/oc/cvs cvs –z9 co or1k/or1200/
Hardware/Software co-design para aplicações de processamento de voz 39
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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 auto-teste 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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 top-
level 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
Pedro Mota | Pedro Santos
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 sub-
módulos incluem memória para tag.
OR1200_RF or1200_rf Bloco que instancia as memórias
usados para a transferência de dados
Or1k Top-level12121 slices
QMEM (8KB)
3637slices
Inst Cache(4kb)
2146 slices
Data Cache (4kb)
2473 slices
Power Management
7 slices
Debug Unit
40 slices
Store Buffer
273 slices
PIC42 slices
Inst Wishboneinterface
90 slices
Data wishbone interface
44 slices
IMMU
150 slices
IMMU TABLE
150 slices
Tick-Timer9 slices
RAM 64x21
114 slices
Tag Cache282 slices
RAM 64x1468 slices
DMMU198 slices
RAM 64x1468 slices
RAM 64x24 103 slices
DMMU TABLE161 slices
Cache Ram ( 4KB)
1726 slices
Tag ram98 slices
Dc ram 4KB1726 Slices
Inst Tag
282 slices
Tag ram 98 slices
CPU CORE3237 slices
Hardware/Software co-design para aplicações de processamento de voz 46
Pedro Mota | Pedro Santos
entre registos e CPU
OR1200_CPU or1200_mult_mac CPU que existe no OpenRISC1200
OR1200_SB or1200_sb Store buffer
OR1200_DU OR1200_DU Unidade que permite o debug dos
programas em tempo real.
OR1200_PIC OR1200_PIC
Módulo que atende as interrupções
que venham a ocorrer no interior do
CPU ou vindas do exterior.
OR1200_TT OR1200_TT Tick-timer do sistema. Usado para
gerar interrupções periódicas.
OR1200_PM OR1200_PM Módulo responsável pelo controlo
dinâmico da potência no Or1k
OR1200_QMEM_TOP OR1200_QMEM_TOP
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
Pedro Mota | Pedro Santos
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:
`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 //
8 `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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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.
Figura 13: Configuração do OpenRisc 1200 no seu top-level e interface com o exterior
CPU/DSP
CORE
Inst. Cache
Data Cache
Wishbone interface
Wishbone interface
Wishbone interface
Wishbone interface
Or1k top
Hardware/Software co-design para aplicações de processamento de voz 50
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
--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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
} > 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
[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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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.
Comando Funcionalidade no simulador
q Abandonar o simulador
r Mostra os registos GPRS
t Executa a próxima instrução
run ex; run 10; run 10 hush
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
Mostra o conteúdo da posição de memória
0x0000_2000 à posição 0x0000_4000
pm endereço valor pm 0x0000_1234 12 Coloca na posição de memória 0x0000_1234 o valor 12
pc valor
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
info
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.
cm endereço_inicial endereço_final size cm 0x0000_1234 0x0000_4567 0xf
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
break endereço (ou label) break 0xF000_1FAB
break teste
Coloca quando se está a realizar o debug um breakpoint no endereço indicado, ou numa label associada a um determinado endereço.
breaks Mostra que breakpoints é que estão presentes na simulação.
reset Executa o reset do simulador, voltando-se ao início da memória do programa e com os registos limpos.
Stat Permite visualizar as estatísticas associadas
Hardware/Software co-design para aplicações de processamento de voz 64
Pedro Mota | Pedro Santos
às instruções que foram executadas
help Permite ter acesso à ajuda incorporada no simulador.
hist Permite ver que instruções foram 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
Pedro Mota | Pedro Santos
cycles, required for read access, -1 if instance does not support reading 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
Pedro Mota | Pedro Santos
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 Descrição/Activação
MEMORY Define como a memória é gerada e em que blocos está dividida. IMMU Secção que trata da configuração da Immu. No caso presente está inactiva.
DMMU Secção que trata da configuração da Dmmu. No caso presente está inactiva.
IC 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.
DC 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.
SIM
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.
VAPI Secção que faz a configuração da Verification API, usada para verificação avançada do Core. Esta desabilitada na configuração actual.
CPU Esta secção permite definir vários parâmetros para o CPU, como a versão eo registo UPR.
PM Permite activar ou não a unidade que faz o controlo de potência. No presente caso esta encontra-se desactivada.
BPB Nesta secção controlam-se os parâmetros relativos à previsão dos saltos. Agora encontra-se desactivada.
DEBUG Secção destinada configurar o comportamento da unidade de debug que está incorporada no Or1k.
MC Permite realizar a configuração do controlador de memória, como o endereço base. No momento presente este encontra-se activo.
UART 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.
DMA Permite a configuração do controlador de acesso a memória, como o endereço ocupado. Não está activo.
ETHERNET
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.
GPIO 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.
VGA 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
Pedro Mota | Pedro Santos
activada na configuração pretendida.
TICK TIMER Secção que trata da configuração do gerador de ticks e qual a interrupção que está associada ao mesmo. Está desabilitado no presente momento.
FRAME BUFFER
Secção que faz a configuração do frame buffer. Mais uma vez o endereço a que este responde também é um dos parâmetros a configurar. Está inactivo.
KBD Secção que realiza a configuração do teclado. O endereço e o número da interrupção têm de ser controlados.
ATA Faz a configuração do controlador ARA/ATAPI. O endereço é 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.
CUC Configura a unidade de compilação do OpenRisc. Encontra-se desactivada 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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 1Word16 sub (Word16 var1, Word16 var2) Short sub 1Word16 abs_s (Word16 var1) Short abs 1Word16 shl (Word16 var1, Word16 var2) Short shift left 1Word16 shr (Word16 var1, Word16 var2) Short shift right 1Word16 mult (Word16 var1, Word16 var2) Short mult 1Word32 L_mult (Word16 var1, Word16 var2) Long mult 1Word16 negate (Word16 var1) Short negate 1Word16 extract_h (Word32 L_var1) Extract high 1Word16 extract_l (Word32 L_var1) Extract low 1Word16 round (Word32 L_var1) Round 1Word32 L_mac (Word32 L_var3, Word16 var1, Word16 var2) Mac 1Word32 L_msu (Word32 L_var3, Word16 var1, Word16 var2) Msu 1Word32 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 2Word32 L_sub (Word32 L_var1, Word32 L_var2) Long sub 2Word32 L_add_c (Word32 L_var1, Word32 L_var2) Long add with c 2Word32 L_sub_c (Word32 L_var1, Word32 L_var2) Long sub with c 2Word32 L_negate (Word32 L_var1) Long negate 2Word16 mult_r (Word16 var1, Word16 var2) Mult with round 2Word32 L_shl (Word32 L_var1, Word16 var2) Long shift left 2Word32 L_shr (Word32 L_var1, Word16 var2) Long shift right 2Word16 shr_r (Word16 var1, Word16 var2) Shift right with round 2Word16 mac_r (Word32 L_var3, Word16 var1, Word16 var2) Mac with rounding 2Word16 msu_r (Word32 L_var3, Word16 var1, Word16 var2) Msu with rounding 2Word32 L_deposit_h (Word16 var1) 16 bit var1 -> MSB 2Word32 L_deposit_l (Word16 var1) 16 bit var1 -> LSB 2Word32 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
Pedro Mota | Pedro Santos
round Word32 L_abs (Word32 L_var1) Long abs 3Word32 L_sat (Word32 L_var1) Long saturation 4Word16 norm_s (Word16 var1) Short norm 15Word16 div_s (Word16 var1, Word16 var2) Short division 18Word16 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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 sem dtx 12,125 12,176 12,274 Codificador com dtx 9,12 12,521 - sem dtx 1,854 1,942 1,957 Descodificador 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
Pedro Mota | Pedro Santos
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,274Tabela 16: Peso computacional do codificador de voz AMR no modo 12.2 kbit/s em
WMOPS.
Average WorstCase WorstWCLSF 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,957Tabela 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
0 20 40 60 80-5
-4
-3
-2
-1
0
1
2Relação sinal ruído do codec
S/N
(dB
)
Atraso da saída em relação à entrada
originalmodificado
0 20 40 60 80-6
-5
-4
-3
-2
-1
0
1
2Relação sinal ruído do codec
S/N
(dB
)
Atraso da saída em relação à entrada
originalmodificado
0 20 40 60 80-5
-4
-3
-2
-1
0
1
2
3
4
5Relação sinal ruído do codec
S/N
(dB
)
Atraso da saída em relação à entrada
originalmodificado
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:
Hardware/Software co-design para aplicações de processamento de voz 78
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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).
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.
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%
Hardware/Software co-design para aplicações de processamento de voz 80
Pedro Mota | Pedro Santos
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 000wxyz0000001wxyza 001wxyz000001wxyzab 010wxyz00001wxyzabc 011wxyz0001wxyzabcd 100wxyz001wxyzabcde 101wxyz01wxyzabcdef 110wxyz1wxyzabcdefg 111wxyz
Tabela 20: Codificação definida pela lei A.
Hardware/Software co-design para aplicações de processamento de voz 81
Pedro Mota | Pedro Santos
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 construiu-
se 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
CPU/DSP
CORE
Inst. Cache
Data Cache
Wishbone interface
Wishbone interface
Wishbone interface
Wishbone interface
Or1k top
Dispositivo que permite o acesso do
Or1k à memoria
através do protocolo Wishbone
Cypress
CY7C
1340A
DATA
Control
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.
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
Pedro Mota | Pedro Santos
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
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.
Memory
Controller
Wishbone
Modelo de um master
que comunica
em wishbone
Ram
Data
Addr
Contro
Hardware/Software co-design para aplicações de processamento de voz 86
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
select 1’b1
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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.
Memory
Controller
Wishbone
Modelo de um master
que comunica
em wishbone
Modelo da
Ram
Data
Addr
Contro
Hardware/Software co-design para aplicações de processamento de voz 93
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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. csc_data = { 8'h00, // reserved SSRAM_SEL, // SEL 4'h0, // reserved 1'b1, // parity enabled 1'b0, // KRO, no meaning for ssram 1'b0, // BAS, no meaning for ssram 1'b0, // WP 2'b00, // MS, no meaning for ssram 2'b10, // BW == 32bit bus. Always for ssram (maybe hardwire ???) 3'b001, // MEM_TYPE == SDRAM 1'b1 // EN == chip select enabled }; // tms_data is unused for ssrams tms_data = { 32'hx }; // 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
[ACK_I] The acknowledge input [ACK_I], when asserted, indicates the
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 The lock output [LOCK_O] when asserted, indicates that the
current bus cycle is uninterruptible.
RTY_I
The retry input [RTY_I] indicates that the interface is not ready to
accept or send data, and that the cycle should be retried.
SEL_O()
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.
STB_O
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
Pedro Mota | Pedro Santos
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
Leitura
CLOCK EDGE 0: 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
.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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
Figura 39: Modelo Master Model para teste
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
Memory
Controller
WishboneModelo
do master 2
Ram
Data
Addr
Contro
Master Slave
Modelo do
master 1 Wishbone
Wishbone
Hardware/Software co-design para aplicações de processamento de voz 106
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
• 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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 recorrendo-
se à 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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 \n"\ "l.movhi\t%3,0x4000 \n"\ "l.sfeq\t%3,%0 \n"\ "l.bnf\t3 \n"\ "l.srai\t%0,%0,0x000f \n"\ "l.addi\t%0,r0,0x7fff \n"\ : "=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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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 criou-
se 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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 \n"\ "l.sfltsi\t%3,0x0 \n"\ "l.bf\t10 \n"\ "l.add\t%0,%1,%2 \n"\ "l.xor\t%3,%1,%0 \n"\ "l.sfgesi\t%3,0x0 \n"\ "l.bf\t6 \n"\ "l.sfgesi\t%1,0x0 \n"\ "l.movhi\t%3,0x7fff \n"\ "l.ori\t%3,%3,0xffff \n"\ "l.movhi\t%4,0x8000 \n"\ "l.cmov\t%0,%3,%4 \n"\ : "=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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
É 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
}; /* 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
/* 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
Anexo 3: Implementação da lei A em código C /* ******************************************************************************** * * A-LAW (ITU-G711) * * a_law.h * * Pedro Santos - [email protected] * Pedro Mota - [email protected] * * 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
Pedro Mota | Pedro Santos
/* ******************************************************************************** * * A-LAW (ITU-G711) * * a_law.c * * Pedro Santos - [email protected] * Pedro Mota - [email protected] * * 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
t += 0x108; /* se a saida fosse de 13-bits seria 0x21 */ t <<= seg - 1; } else 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
Pedro Mota | Pedro Santos
/* ***************************************************************************** * 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
Pedro Mota | Pedro Santos
/* ***************************************************************************** * 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
Pedro Mota | Pedro Santos
# 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
Pedro Mota | Pedro Santos
Anexo 4:Top level do CPU
Hardware/Software co-design para aplicações de processamento de voz 153
Pedro Mota | Pedro Santos
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]
SR[CY] < - carry
SR[OV] < - overflow
Range Exception
l.addc rD,rA,rB Add Signed and Carry rD[31:0] < - rA[31:0] + rB[31:0] + SR[CY]
SR[CY] < - carry
SR[OV] < - overflow
Range Exception
l.addi rD,rA,I Add Immediate Signed rD[31:0] < - rA[31:0] + exts(Immediate)
SR[CY] < - carry
SR[OV] < - overflow
Range Exception
l.addic rD,rA,I Add Immediate Signed and Carry rD[31:0] < - rA[31:0] + exts(Immediate) + SR[CY]
SR[CY] < - carry
SR[OV] < - overflow
Range Exception
l.div rD,rA,rB Divide Signed rD[31:0] < - rA[31:0] / rB[31:0]
SR[OV] < - overflow
SR[CY] < - carry
Range Exception
l.divu rD,rA,rB Divide Unsigned rD[31:0] < - rA[31:0] / rB[31:0]
SR[OV] < - overflow
SR[CY] < - carry
Range Exception
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
Pedro Mota | Pedro Santos
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]
None
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]
None
l.mul rD,rA,rB Multiply Signed rD[31:0] < - rA[31:0] * rB[31:0]
SR[OV] < - overflow
SR[CY] < - carry
Range Exception
l.muli rD,rA,I Multiply Immediate Signed rD[31:0] < - rA[31:0] * Immediate
SR[OV] < - overflow
SR[CY] < - carry
Range Exception
l.mulu rD,rA,rB Multiply Unsigned rD[31:0] < - rA[31:0] * rB[31:0]
SR[OV] < - overflow
SR[CY] < - carry
Range Exception
l.sub rD,rA,rB Subtract Signed rD[31:0] < - rA[31:0] - rB[31:0]
SR[CY] < - carry
SR[OV] < - overflow
Range Exception
Hardware/Software co-design para aplicações de processamento de voz 155
Pedro Mota | Pedro Santos
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]
rD[31:32-rB[4:0]] < - rA[rB[4:0]-1:0] None
l.rori rD,rA,L Rotate Right with Immediate rD[31-L:0] < - rA[31:L] rD[31:32-L] < - rA[L-1:0]
None
l.sll rD,rA,rB Shift Left Logical rD[31:rB[4:0]] < - rA[31-rB[4:0]:0] rD[rB[4:0]-1:0] < - 0
None
l.slli rD,rA,L Shift Left Logical with Immediate rD[31:L] < - rA[31-L:0] rD[L-1:0] < - 0
None
l.sra rD,rA,rB Shift Right Arithmetic rD[31-rB[4:0]:0] < - rA[31:rB[4:0]] rD[31:32-rB[4:0]] < - rA[31]
None
l.srai rD,rA,L Shift Right Arithmetic with Immediate rD[31-L:0] < - rA[31:L] rD[31:32-L] < - rA[31]
None
l.srl rD,rA,rB Shift Right Logical rD[31-rB[4:0]:0] < - rA[31:rB[4:0]] rD[31:32-rB[4:0]] < - 0
None
l.srli rD,rA,L Shift Right Logical with Immediate rD[31-L:0] < - rA[31:L] rD[31:32-L] < - 0
None
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
Pedro Mota | Pedro Santos
Branch Instructions l.bf N Branch if Flag EA < - exts(Immediate < < 2) + BranchInsnAddr
PC < - EA if SR[F] set None
l.bnf N Branch if No Flag EA < - exts(Immediate < < 2) + BranchInsnAddr PC < - EA if SR[F] cleared
None
l.j N Jump PC < - exts(Immediate < < 2) + JumpInsnAddr None l.jal N Jump and Link PC < - exts(Immediate < < 2) + JumpInsnAddr
LR < - DelayInsnAddr + 4 None
l.jalr rB Jump and Link Register PC < - rB LR < - DelayInsnAddr + 4
None
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
Pedro Mota | Pedro Santos
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]
rD[7:0] < - rA[7:0] None
l.extbz rD,rA Extend Byte with Zero rD[31:8] < - 0 rD[7:0] < - rA[7:0]
None
l.exths rD,rA Extend Half Word with Sign rD[31:16] < - rA[15] rD[15:0] < - rA[15:0]
None
l.exthz rD,rA Extend Half Word with Zero rD[31:16] < - 0 rD[15:0] < - rA[15:0]
None
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
rD[31:0] < - MACLO[31:0] MACLO[31:0], MACHI[31:0] <- 0
None
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
Unsigned SR[F] < - rA[31:0] >= extz(Immediate) None
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
Signed SR[F] < - rA[31:0] > exts(Immediate) None
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
Unsigned SR[F] < - rA[31:0] > extz(Immediate) None
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
Pedro Mota | Pedro Santos
l.sflesi rA,I Set Flag if Less or Equal Than Immediate Signed
SR[F] < - rA[31:0] < = exts(Immediate) None
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]
TLB miss Page fault Bus error
l.lbz rD,I(rA) Load Byte and Extend with Zero EA < - exts(Immediate) + rA[31:0] rD[7:0] < - (EA)[7:0] rD[31:8] < - 0
TLB miss Page fault Bus error
l.lhs rD,I(rA) Load Half Word and Extend with Sign
EA < - exts(Immediate) + rA[31:0] rD[15:0] < - (EA)[15:0] rD[31:16] < - (EA)[15]
TLB miss Page fault Bus error Alignment
l.lhz rD,I(rA) Load Half Word and Extend with Zero
EA < - exts(Immediate) + rA[31:0] rD[15:0] < - (EA)[15:0] rD[31:16] < - 0
TLB miss Page fault Bus error Alignment
l.lws rD,I(rA) Load Single Word and Extend wit h Sign
EA < - exts(Immediate) + rA[31:0] rD[31:0] < - (EA)[31:0]
TLB miss Page fault Bus error Alignment
l.lwz rD,I(rA) Load Single Word and Extend with Zero
EA < - exts(Immediate) + rA[31:0] rD[31:0] < - (EA)[31:0]
TLB miss Page fault Bus error
Hardware/Software co-design para aplicações de processamento de voz 159
Pedro Mota | Pedro Santos
Alignment
l.sb I(rA),rB Store Byte EA < - exts(Immediate) + rA[31:0] (EA)[7:0] < - rB[7:0]
TLB miss Page fault Bus error
l.sh I(rA),rB Store Half Word EA < - exts(Immediate) + rA[31:0] (EA)[15:0] < - rB[15:0]
TLB miss Page fault Bus error Alignment
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 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
Pedro Mota | Pedro Santos
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 \n"\ "l.movhi\t%3,0x4000 \n"\ "l.sfeq\t%3,%0 \n"\ "l.bnf\t3 \n"\ "l.srai\t%0,%0,0x000f \n"\ "l.addi\t%0,r0,0x7fff \n"\ : "=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 \n"\ "l.movhi\t%3,0x4000 \n"\ "l.sfeq\t%3,%0 \n"\ "l.bnf\t3 \n"\ "l.srai\t%0,%0,0x000f \n"\ "l.addi\t%0,r0,0x7fff \n"\ : "=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 \n"\ "l.movhi\t%3,0x4000 \n"\ "l.sfeq\t%3,%0 \n"\ "l.bnf\t3 \n"\ "l.slli\t%0,%0,0x1 \n"\ "l.addi\t%0,%0,0xffff \n"\ : "=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 \n"\ "l.movhi\t%3,0x4000 \n"\ "l.sfeq\t%3,%0 \n"\ "l.bnf\t3 \n"\ "l.slli\t%0,%0,0x1 \n"\ "l.addi\t%0,%0,0xffff \n"\ : "=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 \n"\ "l.sfeq\t%1,%2 \n"\ "l.bnf\t4 \n"\ "l.sub\t%0,r0,%1 \n"\ "l.movhi\t%0,0x7fff \n"\ "l.ori\t%0,%0,0xffff \n"\ : "=r" (L_var_out) \ : "r" (L_var1) , "r" (aux1) );\ } #define negate_OR32(var_out, var1){\ asm volatile ( "l.sfeqi\t%1,0x8000 \n"\ "l.bnf\t3 \n"\ "l.sub\t%0,r0,%1 \n"\ "l.ori\t%0,%0,0x7fff \n"\ : "=r" (var_out) \ : "r" (var1) ); \ } #define abs_s_OR32(var_out, var1){\ asm volatile ( "l.sfgesi\t%1,0x0000 \n"\ "l.bf\t6 \n"\ "l.extws\t%0,%1 \n"\
Hardware/Software co-design para aplicações de processamento de voz 161
Pedro Mota | Pedro Santos
"l.sfeqi\t%1,0x8000 \n"\ "l.bnf\t3 \n"\ "l.sub\t%0,r0,%1 \n"\ "l.addi\t%0,r0,0x7fff\n"\ : "=r" (var_out) \ : "r" (var1) ); \ } #define L_abs_OR32(L_var_out, L_var1){\ Word32 aux1;\ asm volatile ( "l.sfgesi\t%1,0x0000 \n"\ "l.bf\t7 \n"\ "l.extws\t%0,%1 \n"\ "l.movhi\t%2,0x8000 \n"\ "l.sfeq\t%1,%2 \n"\ "l.bnf\t3 \n"\ "l.sub\t%0,r0,%1 \n"\ "l.addi\t%0,%0,0xffff\n"\ : "=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 \n"\ : : "r" (__aux1) );\ } #define L_mac_OR32(var1, var2){\ asm volatile ( "l.mac\t%0,%1 \n"\ : : "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 \n"\ "l.sfgtsi\t%3,0x0 \n"\ "l.bf\t10 \n"\ "l.mfspr\t%0,r0,%2 \n"\ "l.movhi\t%3,0x4000 \n"\ "l.sfges\t%0,%3 \n"\ "l.bf\t6 \n"\ "l.sfltsi\t%0,0x0 \n"\ "l.bf\t4 \n"\ "l.slli\t%0,%0,0x1 \n"\ "l.j\t4 \n"\ "l.nop\t0x0 \n"\ "l.movhi\t%0,0x7fff \n"\ "l.ori\t%0,%0,0xffff \n"\ : "=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 \n"\ "l.sfgtsi\t%3,0x0 \n"\ "l.bf\t19 \n"\ "l.mfspr\t%0,r0,%2 \n"\ "l.movhi\t%3,0x4000 \n"\ "l.sfges\t%0,%3 \n"\ "l.bf\t15 \n"\ "l.slli\t%0,%0,0x1 \n"\ \ "l.xor\t%3,%0,%4 \n"\ "l.sfltsi\t%3,0x0 \n"\ "l.bf\t13 \n"\ "l.add\t%0,%0,%4 \n"\
Hardware/Software co-design para aplicações de processamento de voz 162
Pedro Mota | Pedro Santos
"l.xor\t%3,%4,%0 \n"\ "l.sfgesi\t%3,0x0 \n"\ "l.bf\t9 \n"\ "l.sfgesi\t%4,0x0 \n"\ "l.movhi\t%3,0x7fff \n"\ "l.ori\t%3,%3,0xffff \n"\ "l.movhi\t%5,0x8000 \n"\ \ "l.j\t4 \n"\ "l.cmov\t%0,%3,%5 \n"\ "l.movhi\t%0,0x7fff \n"\ "l.ori\t%0,%0,0xffff \n"\ : "=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 \n"\ "l.sfgtsi\t%3,0x0 \n"\ "l.bf\t13 \n"\ "l.sfltsi\t%3,0xffff \n"\ "l.bf\t14 \n"\ "l.mfspr\t%0,r0,%2 \n"\ "l.movhi\t%3,0x4000 \n"\ "l.sfges\t%0,%3 \n"\ "l.bf\t7 \n"\ "l.movhi\t%3,0xc000 \n"\ "l.sfles\t%0,%3 \n"\ "l.bf\t7 \n"\ "l.slli\t%0,%0,0x1 \n"\ "l.j\t6 \n"\ "l.nop\t0x0 \n"\ "l.movhi\t%0,0x7fff \n"\ "l.j\t3 \n"\ "l.ori\t%0,%0,0xffff \n"\ "l.movhi\t%0,0x8000 \n"\ : "=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\n"\ "l.sfgtsi\t%3,0x0 \n"\ "l.bf\t13 \n"\ "l.sfltsi\t%3,0xffff\n"\ "l.bf\t14 \n"\ "l.mfspr\t%0,r0,%2\n"\ "l.movhi\t%3,0x4000 \n"\ "l.sfges\t%0,%3 \n"\ "l.bf\t18 \n"\ "l.movhi\t%3,0xc000 \n"\ "l.sfles\t%0,%3 \n"\ "l.bf\t18 \n"\ "l.slli\t%0,%0,0x1\n"\ \ "l.xor\t%3,%0,%4 \n"\ "l.sfltsi\t%3,0x0 \n"\ "l.bf\t15 \n"\ "l.add\t%0,%0,%4 \n"\ "l.xor\t%3,%4,%0 \n"\ "l.sfgesi\t%3,0x0 \n"\ "l.bf\t11 \n"\ "l.sfgesi\t%4,0x0 \n"\ "l.movhi\t%3,0x7fff \n"\ "l.ori\t%3,%3,0xffff\n"\ "l.movhi\t%5,0x8000 \n"\ \ "l.j\t6 \n"\ "l.cmov\t%0,%3,%5 \n"\ "l.movhi\t%0,0x7fff \n"\ "l.j\t3 \n"\ "l.ori\t%0,%0,0xffff\n"\ "l.movhi\t%0,0x8000 \n"\ : "=r" (L_var_out) \
Hardware/Software co-design para aplicações de processamento de voz 163
Pedro Mota | Pedro Santos
: "K" (SPR_MACHI) , "K" (SPR_MACLO) , "r" (__aux1) , "r" (L_var1) , "r" (__aux2));\ } #define L_mac_out_round_OR32(var_out){\ Word32 __aux1=0;\ asm volatile ( "l.mfspr\t%3,r0,%1 \n"\ "l.sfgtsi\t%3,0x0 \n"\ "l.bf\t16 \n"\ "l.sfltsi\t%3,0xffff \n"\ "l.bf\t17 \n"\ "l.mfspr\t%0,r0,%2 \n"\ "l.movhi\t%3,0x3fff \n"\ "l.ori\t%3,%3,0xc000 \n"\ "l.sfges\t%0,%3 \n"\ "l.bf\t9 \n"\ "l.movhi\t%3,0xc000 \n"\ "l.sfles\t%0,%3 \n"\ "l.bf\t9 \n"\ "l.slli\t%0,%0,0x1 \n"\ "l.ori\t%3,r0,0x8000 \n"\ "l.add\t%0,%0,%3 \n"\ "l.j\t6 \n"\ "l.srai\t%0,%0,0x10 \n"\ "l.addi\t%0,r0,0x7fff \n"\ "l.j\t3 \n"\ "l.nop 0x0 \n"\ "l.addi\t%0,r0,0x8000 \n"\ : "=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 \n"\ "l.sfgtsi\t%3,0x0 \n"\ "l.bf\t16 \n"\ "l.sfltsi\t%3,0xffff \n"\ "l.bf\t17 \n"\ "l.mfspr\t%0,r0,%2 \n"\ "l.movhi\t%3,0x3fff \n"\ "l.ori\t%3,%3,0xc000 \n"\ "l.sfges\t%0,%3 \n"\ "l.bf\t9 \n"\ "l.movhi\t%3,0xc000 \n"\ "l.sfles\t%0,%3 \n"\ "l.bf\t9 \n"\ "l.slli\t%0,%0,0x1 \n"\ "l.ori\t%3,r0,0x8000 \n"\ "l.add\t%0,%0,%3 \n"\ "l.j\t6 \n"\ "l.srai\t%0,%0,0x10 \n"\ "l.addi\t%0,r0,0x7fff \n"\ "l.j\t3 \n"\ "l.nop 0x0 \n"\ "l.addi\t%0,r0,0x8000 \n"\ : "=r" (var_out) \ : "K" (SPR_MACHI) , "K" (SPR_MACLO) , "r" (__aux1) );\ } /* --> fim instruções MAC <-- */ #define add_OR32(var_out, var1, var2){\ volatile Word32 __aux1=0;\ volatile Word32 __aux2=var1;\ asm volatile ( \ \ "l.xor\t%3,%1,%2 \n"\ "l.sfltsi\t%3,0x0 \n"\ "l.bf\t10 \n"\ "l.add\t%0,%1,%2 \n"\ "l.exths\t%0,%0 \n"\ "l.xor\t%3,%4,%0 \n"\ "l.sfgesi\t%3,0x0 \n"\ "l.bf\t5 \n"\ "l.sfgesi\t%1,0x0 \n"\ "l.addi\t%3,r0,0x7fff \n"\ "l.addi\t%4,r0,0x8000 \n"\
Hardware/Software co-design para aplicações de processamento de voz 164
Pedro Mota | Pedro Santos
"l.cmov\t%0,%3,%4 \n"\ : "=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 \n"\ "l.sfgesi\t%3,0x0 \n"\ "l.bf\t10 \n"\ "l.sub\t%0,%1,%2 \n"\ "l.exths\t%0,%0 \n"\ "l.xor\t%3,%4,%0 \n"\ "l.sfgesi\t%3,0x0 \n"\ "l.bf\t5 \n"\ "l.sfgesi\t%1,0x0 \n"\ "l.addi\t%3,r0,0x7fff \n"\ "l.addi\t%4,r0,0x8000 \n"\ "l.cmov\t%0,%3,%4 \n"\ : "=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 \n"\ "l.sfltsi\t%3,0x0 \n"\ "l.bf\t10 \n"\ "l.addi\t%0,%1,%2 \n"\ "l.exths\t%0,%0 \n"\ "l.xor\t%3,%4,%0 \n"\ "l.sfgesi\t%3,0x0 \n"\ "l.bf\t5 \n"\ "l.sfgesi\t%1,0x0 \n"\ "l.addi\t%3,r0,0x7fff \n"\ "l.addi\t%4,r0,0x8000 \n"\ "l.cmov\t%0,%3,%4 \n"\ : "=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 \n"\ "l.ori\t%4,%1,0x0 \n"\ "l.addi\t%3,r0,0xffff \n"\ "l.addi\t%3,%3,0x1 \n"\ "l.sfltsi\t%3,0xf \n"\ "l.bnf\t8 \n"\ "l.slli\t%4,%4,0x1 \n"\ "l.sfges\t%4,%2 \n"\ "l.bnf\t-5 \n"\ "l.slli\t%0,%0,0x1 \n"\ "l.sub\t%4,%4,%2 \n"\ "l.j\t-8 \n"\ "l.addi\t%0,%0,0x1 \n"\ : "=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 \n"\ : "=r" (var_out) \ : "r" (var1) , "r" (var2) );\ } #define shr_i_OR32(var_out, var1, var2){\ asm volatile ( "l.srai\t%0,%1,%2 \n"\ : "=r" (var_out) \
Hardware/Software co-design para aplicações de processamento de voz 165
Pedro Mota | Pedro Santos
: "r" (var1) , "i" (var2) );\ } #define L_shr_OR32(L_var_out, L_var1, var2){\ Word32 __aux1 = 0x1f;\ asm volatile ( "l.sfgtsi\t%2,0x1f \n"\ "l.cmov\t%3,%3,%2 \n"\ "l.sra\t%0,%1,%3 \n"\ : "=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 \n"\ : "=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 \n"\ "l.sfltsi\t%3,0x0 \n"\ "l.bf\t10 \n"\ "l.add\t%0,%1,%2 \n"\ "l.xor\t%3,%4,%0 \n"\ "l.sfgesi\t%3,0x0 \n"\ "l.bf\t6 \n"\ "l.sfgesi\t%1,0x0 \n"\ "l.movhi\t%3,0x7fff \n"\ "l.ori\t%3,%3,0xffff \n"\ "l.movhi\t%4,0x8000 \n"\ "l.cmov\t%0,%3,%4 \n"\ : "=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 \n"\ "l.sfltsi\t%3,0x0 \n"\ "l.bf\t10 \n"\ "l.addi\t%0,%1,%2 \n"\ "l.xor\t%3,%4,%0 \n"\ "l.sfgesi\t%3,0x0 \n"\ "l.bf\t6 \n"\ "l.sfgesi\t%1,0x0 \n"\ "l.movhi\t%3,0x7fff \n"\ "l.ori\t%3,%3,0xffff \n"\ "l.movhi\t%4,0x8000 \n"\ "l.cmov\t%0,%3,%4 \n"\ : "=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 \n"\ "l.sfgesi\t%3,0x0 \n"\ "l.bf\t10 \n"\ "l.sub\t%0,%1,%2 \n"\ "l.xor\t%3,%4,%0 \n"\ "l.sfgesi\t%3,0x0 \n"\ "l.bf\t6 \n"\ "l.sfgesi\t%1,0x0 \n"\ "l.movhi\t%3,0x7fff \n"\ "l.ori\t%3,%3,0xffff \n"\ "l.movhi\t%4,0x8000 \n"\ "l.cmov\t%0,%3,%4 \n"\ : "=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
Pedro Mota | Pedro Santos
Word32 __aux3;\ asm volatile ( "l.sflesi\t%2,0x0 \n"\ "l.bf\t20 \n"\ "l.cmov\t%0,%1,%1 \n"\ "l.movhi\t%3,0x3fff \n"\ "l.ori\t%3,%3,0xffff \n"\ "l.movhi\t%4,0xc000 \n"\ "l.cmov\t%5,%2,%2 \n"\ "l.sfges\t%0,%3 \n"\ "l.bf\t8 \n"\ "l.sfles\t%0,%4 \n"\ "l.bf\t6 \n"\ "l.addi\t%5,%5,0xffff \n"\ "l.sfgtsi\t%5,0x0 \n"\ "l.bf\t-6 \n"\ "l.slli\t%0,%0,0x1 \n"\ "l.j\t6 \n"\ "l.sfgesi\t%0,0x0 \n"\ "l.movhi\t%3,0x7fff \n"\ "l.ori\t%3,%3,0xffff \n"\ "l.movhi\t%4,0x8000 \n"\ "l.cmov\t%0,%3,%4 \n"\ : "=r" (L_var_out) \ : "r" (L_var1) , "r" (var2) , "r" (__aux1) , "r" (__aux2) , "r" (__aux3));\ } #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 \n"\ "l.bf\t20 \n"\ "l.cmov\t%0,%1,%1 \n"\ "l.movhi\t%3,0x3fff \n"\ "l.ori\t%3,%3,0xffff\n"\ "l.movhi\t%4,0xc000 \n"\ "l.cmov\t%5,%2,%2 \n"\ "l.sfges\t%0,%3 \n"\ "l.bf\t8 \n"\ "l.sfles\t%0,%4 \n"\ "l.bf\t6 \n"\ "l.addi\t%5,%5,0xffff\n"\ "l.sfgtsi\t%5,0x0 \n"\ "l.bf\t-6 \n"\ "l.slli\t%0,%0,0x1\n"\ "l.j\t6 \n"\ "l.sfgesi\t%0,0x0 \n"\ "l.movhi\t%3,0x7fff \n"\ "l.ori\t%3,%3,0xffff\n"\ "l.movhi\t%4,0x8000 \n"\ "l.cmov\t%0,%3,%4 \n"\ \ "l.srai\t%0,%0,0xf\n"\ "l.addi\t%0,%0,0x1\n"\ "l.srai\t%0,%0,0x1\n"\ "l.sfgtsi\t%0,0x7fff\n"\ "l.bnf\t3 \n"\ "l.nop\t0x0 \n"\ "l.addi\t%0,r0,0x7fff\n"\ : "=r" (var_out) \ : "r" (L_var1) , "r" (var2) , "r" (__aux1) , "r" (__aux2) , "r" (__aux3));\ } #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 \n"\ "l.bf\t20 \n"\ "l.cmov\t%0,%1,%1 \n"\ "l.movhi\t%3,0x3fff \n"\
Hardware/Software co-design para aplicações de processamento de voz 167
Pedro Mota | Pedro Santos
"l.ori\t%3,%3,0xffff\n"\ "l.movhi\t%4,0xc000 \n"\ "l.cmov\t%5,%2,%2 \n"\ "l.sfges\t%0,%3 \n"\ "l.bf\t8 \n"\ "l.sfles\t%0,%4 \n"\ "l.bf\t6 \n"\ "l.addi\t%5,%5,0xffff\n"\ "l.sfgtsi\t%5,0x0 \n"\ "l.bf\t-6 \n"\ "l.slli\t%0,%0,0x1\n"\ "l.j\t6 \n"\ "l.sfgesi\t%0,0x0 \n"\ "l.movhi\t%3,0x7fff \n"\ "l.ori\t%3,%3,0xffff\n"\ "l.movhi\t%4,0x8000 \n"\ "l.cmov\t%0,%3,%4 \n"\ \ "l.srai\t%0,%0,0x10\n"\ : "=r" (var_out) \ : "r" (L_var1) , "r" (var2) , "r" (__aux1) , "r" (__aux2) , "r" (__aux3));\ } #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 \n"\ : "=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 \n"\ "l.srai\t%0,%0,0x10 \n"\ : "=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 \n"\ \ "l.srai\t%0,%0,0xf \n"\ "l.addi\t%0,%0,0x1 \n"\ "l.srai\t%0,%0,0x1 \n"\ "l.sfgtsi\t%0,0x7fff \n"\ "l.bnf\t3 \n"\ "l.nop\t0x0 \n"\ "l.addi\t%0,r0,0x7fff \n"\ : "=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 \n"\ "l.cmov\t%3,%3,%2 \n"\ "l.sll\t%0,%1,%3 \n"\ "l.sfgtsi\t%0,0x7fff \n"\ "l.bf\t3 \n"\ "l.sfltsi\t%0,0x8000 \n"\ "l.bnf\t5 \n"\ "l.sfgesi\t%1,0x0 \n"\ "l.addi\t%3,r0,0x7fff \n"\ "l.addi\t%4,r0,0x8000 \n"\ "l.cmov\t%0,%3,%4 \n"\ : "=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 \n"\ "l.sfgtsi\t%0,0x7fff \n"\
Hardware/Software co-design para aplicações de processamento de voz 168
Pedro Mota | Pedro Santos
"l.bf\t3 \n"\ "l.sfltsi\t%0,0x8000 \n"\ "l.bnf\t5 \n"\ "l.sfgesi\t%1,0x0 \n"\ "l.addi\t%3,r0,0x7fff \n"\ "l.addi\t%4,r0,0x8000 \n"\ "l.cmov\t%0,%3,%4 \n"\ : "=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 \n"\ : "=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 \n"\ : "=r" (var_out) \ : "r" (var1) , "i" (var2) );\ } #define norm_s_OR32(var_out, var1){\ Word32 __aux1;\ asm volatile ( "l.sfeqi\t%1,0x0 \n"\ "l.bf\t14 \n"\ "l.addi\t%0,r0,0x0 \n"\ "l.sfeqi\t%1,0xffff \n"\ "l.bf\t11 \n"\ "l.addi\t%0,r0,0xf \n"\ "l.cmov\t%2,%1,%1 \n"\ "l.sfltsi\t%1,0x0 \n"\ "l.bnf\t3 \n"\ "l.addi\t%0,r0,0xffff \n"\ "l.xori\t%2,%1,0xffff \n"\ "l.addi\t%0,%0,0x1 \n"\ "l.sfltsi\t%2,0x4000 \n"\ "l.bf\t-2 \n"\ "l.slli\t%2,%2,0x1 \n"\ : "=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 \n"\ "l.sfeqi\t%1,0x0 \n"\ "l.bf\t14 \n"\ "l.addi\t%0,r0,0x0 \n"\ "l.sfeqi\t%1,0xffff \n"\ "l.bf\t11 \n"\ "l.addi\t%0,r0,0x1f \n"\ "l.cmov\t%2,%1,%1 \n"\ "l.sfltsi\t%1,0x0 \n"\ "l.bnf\t3 \n"\ "l.addi\t%0,r0,0xffff \n"\ "l.xori\t%2,%1,0xffff \n"\ "l.addi\t%0,%0,0x1 \n"\ "l.sflts\t%2,%3 \n"\ "l.bf\t-2 \n"\ "l.slli\t%2,%2,0x1 \n"\ : "=r" (var_out) \ : "r" (var1) , "r" (__aux1) , "r" (__aux2) );\ }
Hardware/Software co-design para aplicações de processamento de voz 169
Pedro Mota | Pedro Santos
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 //// //// [email protected] //// //// //// //// //// //// Downloaded from: http://www.opencores.org/cores/mem_ctrl/ //// //// //// ///////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2001, 2002 Richard Herveille //// //// [email protected] //// //// //// //// 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
Pedro Mota | Pedro Santos
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); csc_data = { 8'h00, // reserved SSRAM_SEL, // SEL 4'h0, // reserved 1'b0, // parity enabled 1'b0, // KRO, no meaning for ssram 1'b0, // BAS, no meaning for ssram 1'b0, // WP 2'b00, // MS, no meaning for ssram 2'b10, // BW == 32bit bus. Always for ssram (maybe hardwire ???) 3'b001, // MEM_TYPE == SDRAM 1'b1 // EN == chip select enabled }; // tms_data is unused for ssrams tms_data = { 32'hx }; // 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) // check written data wbm.wb_cmp(0, 0, 32'h6000_0030, csc_data); wbm.wb_cmp(0, 0, 32'h6000_0034, tms_data); cyc_delay = 0; stb_delay = 0; for (cyc_delay = 0; cyc_delay <= MAX_CYC_DELAY; cyc_delay = cyc_delay +1) for (stb_delay = 0; stb_delay <= MAX_STB_DELAY; stb_delay = stb_delay +1) for (bl = 1; bl <= 8 ; bl = bl +1) begin $display("\nSSRAM sequential test. BL = %d, CYC-delay = %d, STB-delay = ", bl, cyc_delay, stb_delay); // 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 -bl) <<2); for (k=0; k < bl; k=k+1) begin // fill destination backwards, but with linear bursts dest_adr = my_adr + (k<<2); tmp0 = ~dest_adr[15:0] + bl + cyc_delay + stb_delay;
Hardware/Software co-design para aplicações de processamento de voz 171
Pedro Mota | Pedro Santos
tmp1 = dest_adr[15:0] + bl + cyc_delay + stb_delay; my_dat = {tmp0, tmp1}; wbm.wb_write(cyc_delay, stb_delay, dest_adr, my_dat); $display (" time = %f operação de leitura e de teste no master 1 \n",$time); wbm.wb_cmp( cyc_delay, stb_delay, dest_adr, my_dat ) ; end end // 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; tmp0 = ~dest_adr[15:0] + bl + cyc_delay + stb_delay; tmp1 = dest_adr[15:0] + bl + cyc_delay + stb_delay; my_dat = {tmp0, tmp1}; wbm.wb_cmp(cyc_delay, stb_delay, dest_adr, my_dat); end*/ end repeat(10) @(posedge wb_clk); //wait a while // show Wishbone-Master-model current-error-counter wbm.show_cur_err_cnt; end endtask // test_ssram_seq
Hardware/Software co-design para aplicações de processamento de voz 172
Pedro Mota | Pedro Santos
Anexo 8: bench.v ///////////////////////////////////////////////////////////////////// //// //// //// OpenCores Memory Controller Testbench //// //// Main testbench //// //// //// //// Author: Richard Herveille //// //// [email protected] //// //// //// //// //// //// Downloaded from: http://www.opencores.org/cores/mem_ctrl/ //// //// //// ///////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2001 Richard Herveille //// //// [email protected] //// //// //// //// 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
Pedro Mota | Pedro Santos
// // 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
Pedro Mota | Pedro Santos
.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
Pedro Mota | Pedro Santos
.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
Pedro Mota | Pedro Santos
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; $stop; end ////////////////////// // // Internal tasks // task run_tests;
Hardware/Software co-design para aplicações de processamento de voz 177
Pedro Mota | Pedro Santos
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 chip-select. // 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(0, 0, 32'h6000_0008, `BA_MASK); // program base address register 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
Pedro Mota | Pedro Santos
//////////////////////////////// // 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
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 `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 `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 `endif `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
Pedro Mota | Pedro Santos
`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 `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 `endif `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 `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 `endif `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
Pedro Mota | Pedro Santos
`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 `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 `endif `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 `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 `endif 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
Pedro Mota | Pedro Santos
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 ce_reg; reg Read_reg; reg Read_reg_o; reg WrN_reg; reg ADSP_N_o; reg pipe_reg; reg bwa_reg; reg bwb_reg; reg bwc_reg; reg bwd_reg; reg Sys_clk; reg test; reg pcsr_write; reg ctlr_write; reg latch_addr_current; reg 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
Pedro Mota | Pedro Santos
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); // Format time unit $readmemh("banco0.mem",bank0); $readmemh("banco1.mem",bank1); $readmemh("banco2.mem",bank2); $readmemh("banco3.mem",bank3); 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 pcsr_write = 1'b0; ctlr_write = 1'b0; end // Write Register if (Read_reg_o == 1'b1) WrN_reg = 1'b1; else WrN_reg = Write_n; 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) // Linear Burst bcount = (bcount + 1); // Advance Counter
Hardware/Software co-design para aplicações de processamento de voz 189
Pedro Mota | Pedro Santos
else if (Mode & ~ADV_N & ~latch_addr) // Interleaved Burst begin if (first_addr % 2 == 0) bcount = (bcount + 1); // Increment Counter else if (first_addr % 2 == 1) bcount = (bcount - 1); // Decrement Counter end // 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
Pedro Mota | Pedro Santos
$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
Pedro Mota | Pedro Santos
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: [email protected] * 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
Pedro Mota | Pedro Santos
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 ce_reg; reg pipe_reg; reg bwa_reg; reg bwb_reg; 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); // Format time unit end 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
Pedro Mota | Pedro Santos
// Enable Register if (clr) ce_reg <= ce; // Pipelined Enable pipe_reg <= ce_reg; end // 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
Pedro Mota | Pedro Santos
endmodule
Hardware/Software co-design para aplicações de processamento de voz 195
Pedro Mota | Pedro Santos
Anexo 13: bench1.v ///////////////////////////////////////////////////////////////////// //// //// //// OpenCores Memory Controller Testbench //// //// Main testbench //// //// //// //// Author: Richard Herveille //// //// [email protected] //// //// //// //// //// //// Downloaded from: http://www.opencores.org/cores/mem_ctrl/ //// //// //// ///////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2001 Richard Herveille //// //// [email protected] //// //// //// //// 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
Pedro Mota | Pedro Santos
// // 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
Pedro Mota | Pedro Santos
// // 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
Pedro Mota | Pedro Santos
.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
Pedro Mota | Pedro Santos
.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
Pedro Mota | Pedro Santos
.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
Pedro Mota | Pedro Santos
.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
Pedro Mota | Pedro Santos
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; $stop; end ////////////////////// // // 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
Pedro Mota | Pedro Santos
// 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 chip-select. // 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
Pedro Mota | Pedro Santos
Anexo 14: bench2.v ///////////////////////////////////////////////////////////////////// //// //// //// OpenCores Memory Controller Testbench //// //// Main testbench //// //// //// //// Author: Richard Herveille //// //// [email protected] //// //// //// //// //// //// Downloaded from: http://www.opencores.org/cores/mem_ctrl/ //// //// //// ///////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2001 Richard Herveille //// //// [email protected] //// //// //// //// 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
// // 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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
(.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
Pedro Mota | Pedro Santos
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
Pedro Mota | Pedro Santos
.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
Pedro Mota | Pedro Santos
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 $display (" Escrita do master 2 \n"); wbm2.wb_write(0, 0, 32'h1000_0000, 32'hff_dd_ee_cc); // program base address register $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); end end */ 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; $stop; end //////////////////////
Hardware/Software co-design para aplicações de processamento de voz 212
Pedro Mota | Pedro Santos
// // 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 chip-select. // 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(0, 0, 32'h6000_0008, `BA_MASK); // program base address register 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
Pedro Mota | Pedro Santos
// 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
Pedro Mota | Pedro Santos
Anexo 15: Or1k TOP level
Hardware/Software co-design para aplicações de processamento de voz 215
Pedro Mota | Pedro Santos
Anexo 16: mc_defines.v ///////////////////////////////////////////////////////////////////// //// //// //// WISHBONE Memory Controller Definitions //// //// //// //// //// //// Author: Rudolf Usselmann //// //// [email protected] //// //// //// //// //// //// Downloaded from: http://www.opencores.org/cores/mem_ctrl/ //// //// //// ///////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2000-2002 Rudolf Usselmann //// //// www.asics.ws //// //// [email protected] //// //// //// //// 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
Pedro Mota | Pedro Santos
// - 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
Pedro Mota | Pedro Santos
// // 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 2'h0 `define MC_BW_16 2'h1 `define MC_BW_32 2'h2 `define MC_MEM_TYPE_SDRAM 3'h0 `define MC_MEM_TYPE_SRAM 3'h1 `define MC_MEM_TYPE_ACS 3'h2 `define MC_MEM_TYPE_SCS 3'h3 `define MC_MEM_SIZE_64 2'h0 `define MC_MEM_SIZE_128 2'h1 `define MC_MEM_SIZE_256 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
Pedro Mota | Pedro Santos
`define MC_ALL_BANKS 1'b1
Hardware/Software co-design para aplicações de processamento de voz 219
Pedro Mota | Pedro Santos
Anexo 17 : Top level do master slave