0% acharam este documento útil (0 voto)
4 visualizações44 páginas

Interrupções e Periféricos no RS5

O documento aborda a arquitetura e organização do RS5, focando no tratamento de interrupções e na integração de periféricos. Ele descreve a implementação de controladores de interrupção, como o CLIC e o PLIC, e detalha o processo de inclusão de periféricos no sistema. Além disso, o documento fornece exemplos práticos de código e referências para a configuração de software e hardware relacionados.

Enviado por

Iuri Gomes
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
4 visualizações44 páginas

Interrupções e Periféricos no RS5

O documento aborda a arquitetura e organização do RS5, focando no tratamento de interrupções e na integração de periféricos. Ele descreve a implementação de controladores de interrupção, como o CLIC e o PLIC, e detalha o processo de inclusão de periféricos no sistema. Além disso, o documento fornece exemplos práticos de código e referências para a configuração de software e hardware relacionados.

Enviado por

Iuri Gomes
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd

43328 - Arquitetura e Organização

21/jan/2025 (tarde)
RS5: periférico e tratamento de
interrupção
Fernando Gehm Moraes
PUCRS
Agenda

RS5: periférico e tratamento de interrupção

1. Hardware: RS5 + PLIC + memória + periférico

2. Software
• Tratamento de interrupção
• “driver”
• Aplicação

3. Validação

4. Exercício

Obrigado ao Rafael Faccenda (pós-doutorando do grupo de pesquisa) 2


Tratamento de Interrupções

Baseado em proposta da SiFive. Dois tipos de controladores de interrupção:


• Controlador Local de Interrupção do Núcleo (CLIC)
• Gerencia interrupções de múltiplos threads de hardware no mesmo núcleo
• Controlador de Interrupção em Nível de Plataforma (PLIC)
• Gerencia interrupções globais ou externas geradas por dispositivos periféricos
• Permite atribuir prioridades configuráveis para cada interrupção

Suporte a interrupções no RS5


• Interrupções de timer
• Através de um Relógio de Tempo Real (RTC)
• Interrupções externas
• Gerenciadas pelo PLIC

O PLIC é configurável e oferece interface para Registradores Mapeados em Memória (MMR)

3
Hardware: top + periférico Visão Geral do Top
TOP
• implementado no [Link] (para síntese ver a pasta proto, que possui uma UART e BRAM)
• 4 módulos: RS5, memória, PLIC, RTC (antes de colocarmos o periférico)
• Lógica de conexão entre os módulos

irq_i (32 bits) e int_ack (assign irq = {20'h0, mei, 3'h0, mti, 7’h0};)
mei
mti

Vetor de interrupção
parametrizável em função
do número de periféricos
Logica de decodificação
de endereços mapeados
em memória

localparam int i_cnt = 1; 4


Top – passo-a-passo pra inclusão do periférico (1/4)
(1) Arquivo [Link] à é no nosso top
(2) Sinais a serem inseridos no [Link] (após assign irq =... Linha ± 120)
• enable_periph, enable_periph_r: sinais que habilitam o processador a ler um dado (_r, de
registrado, pois a CPU pode estar tratando outra interrupção)
• data_periph: dado gerado pelo periférico
• irq_periph, iack_periph: sinais de interrupção (observar o uso do i_cnt à este será o periférico 1)

/* New modulue signals */


logic enable_periph, enable_periph_r;
logic [31:0] data_periph;
logic [i_cnt:1] irq_periph, iack_periph;

• Estes sinas se aplicam a outros periféricos (o método é geral)

5
Top – passo-a-passo pra inclusão do periférico (2/4)
(2) Decodificação do endereço de leitura da CPU
• Permite que a CPU receba dados da memória, rtc, plic e periféricos (4 bits mais significativos do endereço
usados no controle do mux)
always_comb begin
if (mem_operation_enable) begin
if (mem_address[31:28] < 4'h2) begin
enable_ram = 1'b1; (256MB para RAM)
demais enable em 0;
end
else if (mem_address[31:28] < 4'h3) begin
enable_rtc = 1'b0;
demais enable em 0;
end
else if (mem_address[31:28] < 4'h8) begin
enable_plic = 1'b1;
demais enable em 0;
end
else if (mem_address[31:28] < 4'h9) begin
enable_tb = 1'b1;
demais enable em 0;
end
else begin
enable_periph = 1’b1;
enable_periph = 1'b0;
demais enable em 0;
demais enable em 0;
end 6
Top – passo-a-passo pra inclusão do periférico (3/4)
(3) Envio dos dados para a CPU

always_ff @(posedge clk) begin // registra os enable – dado é recebido no ciclo seguinte
enable_tb_r <= enable_tb;
enable_rtc_r <= enable_rtc;
enable_plic_r <= enable_plic;
enable_periph_r <= enable_periph;
end

always_comb begin
if (enable_tb_r) begin
mem_data_read = data_tb;
end
else if (enable_rtc_r) begin
mem_data_read = data_rtc[31:0];
end
else if (enable_plic_r) begin
mem_data_read = data_plic;
end
else if (enable_periph_r) begin
mem_data_read = data_periph;
end
else begin
mem_data_read = data_ram;
end
end
7
Top – passo-a-passo pra inclusão do periférico (4/4)
(4) Instanciar o periférico
• Barramento de endereços (12 bits)
• Recepção de dados: barramento de dados da memória (mem_data_write) e write enable
• Envio de dados : quando enable_periph ativo o dado é colocado em data_periph
• Controle de interrupção (periférico 1)

periph periph(
.clk (clk),
.reset_n (reset_n),
.en_i (enable_periph),
.addr_i (mem_address[11:0]),
.we_i (|mem_write_enable), // ou – reduz we_i de 4 para 1 bit
.data_i (mem_data_write),
.data_o (data_periph),
.irq (irq_periph[1]),
.iack (iack_periph[1])
);

8
Material de Referência
Baixar de:
[Link]

perif_RS5
├── app
│ └── peripheral-test
│ ├── Makefile
│ ├── include
│ │ └── periph.h
│ └── src Aplicação exemplo
│ ├── intr.S
│ ├── main.c
│ ├── periph.c
│ └── trap.c
└── rtl
└── [Link] SV do periférico exemplo

9
Detalhamento do periférico (1/5)
Exemplo prático
(1) Instanciar o periférico
• CPU conectada a um controlador AXI
• Interface externa
• Periféricos conectados ao AXI e ao mundo externo

module periph (
input logic clk,
input logic reset_n,

input logic en_i,


input logic [11:0] addr_i,
input logic we_i,
input logic [31:0] data_i,
output logic [31:0] data_o,

output logic irq,


input logic iack
);

10
Detalhamento do periférico (2/5)
(2) Interrupção enviada ao PLIC
start=0 iack=0
• Controlado por uma máquina de estados

1
t=
ar
st
S_IDLE S_SUM S_INT
always_ff @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
irq <= 1'b0;
end
else begin
if (current_state == S_INT) begin
irq <= 1'b1; iack=1
end
else begin
irq <= 1'b0; • Software ativa o start para iniciar o processamento
end • Periférico executa o processamento no estado S_Sum
end
end (em periféricos reais aqui haverão muitos estados)
• Ao final do processamento o periférico ativa a
interrupção (S_Int)
• O PLIC responde com IACK para o periférico voltar ao
estado S_IDLE
11
Detalhamento do periférico (3/5)
(3) Dados lidos pelo processador
• Decodificação da parte alta ([31:28]≥ 9) dos endereços é realizada no top
• O periférico tem 12 bits como espaço de endereçamento
0x90000300 start
0x900002FF
result
• Endereços devem corresponder ao software 0x90000200
0x900001FF
opB
0x90000100
0x900000FF
opA
0x90000000
always_ff @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
data_o <= ‘0; Quantos opA, opB
end e result o periférico
else if (en_i && we_i == '0) begin pode usar?
unique case (addr_i[11:0])
12'h000: data_o <= opA;
12'h100: data_o <= opB;
12'h200: data_o <= result; // na prática só lerá result
12'h300: data_o <= {31'b0,start};
default: data_o <= ‘0;
endcase
end
end
12
Detalhamento do periférico (4/5)
(4) Dados escritos pelo processador
• Decodificação da parte alta ([31:28]≥ 9) dos endereços é realizada no top
• O periférico tem 12 bits como espaço de endereçamento
0x90000300 start
0x900002FF
result
• Endereços devem corresponder ao software 0x90000200
0x900001FF
opB
0x90000100
always_ff @(posedge clk or negedge reset_n) begin 0x900000FF
if (!reset_n) begin
opA
opA <= ‘0;
0x90000000
opB <= ‘0;
start <= '0;
end
else begin
if (en_i && we_i != '0) begin
unique case (addr_i[11:0])
12'h000: opA <= data_i;
12'h100: opB <= data_i;
Aqui não tem result: porque?
12'h300: start <= data_i[0];
default: ;
endcase
end
end
end
13
Detalhamento do periférico (5/5)
(5) Tarefa executada pelo periférico
• Soma (só para demonstração de funcionalidade)

always_ff@ (posedge clk, negedge reset_n) begin


if (!reset_n) begin
result <= ‘0;
end
else begin
if (current_state == S_SUM) begin
result <= opA + opB;
end
end
end

14
Software para perif. e interrupção
Usaremos C + newlib à crt0.S da newlib, arquivo chamado pelo Makefile

peripheral-test main.c
|-- include
| `-- periph.h
|-- Makefile perif.h e perif.c
`-- src
|-- intr.S intr.S trap.c
|-- main.c
|-- periph.c Hardware
`-- trap.c

15
intr.S

intr.S main.c

• software em assembly que faz a interface com o hardware


perif.h e perif.c
config_intr: configura o processador para realizar o tratamento da interrupção
trap_handler: chamado na interrupção, e por sua vez chama o irq_dispatcher
intr.S trap.c

Hardware

16
Registradores CSR

Oito CSRs tratam exceção no modo de máquina:


• mstatus, Machine Status, permissão global de interrupção
• mip, Machine Interruption Pending, lista as interrupções atualmente pendentes
• mie, Machine Interrupt Enable, define as interrupções o processador pode tomar e
quais deve ignorar
p. 107 em diante
• mcause, Machine Exception Cause, indica qual exceção ocorreu
• mtvec, Machine Trap Vector, endereço para qual o processador salta quando ocorre
uma exceção
• mtval, Machine Trap Value, informações adicionais sobre exceção
• mepc, Machine Exception PC, aponta para a instrução onde a exceção ocorreu
• mscratch, Machine Scratch, armazenamento temporário para tratadores de traps (pode
substituir o sp)

17
intr.S

intr.S main.c

config_intr: configura o processador para realizar o tratamento da interrupção


perif.h e perif.c
trap_handler: chamado na interrupção, e por sua vez chama o irq_dispatcher

intr.S trap.c

config_intr: Hardware
# Configura o tratador de interrupções
la t0, trap_handler # Grava o endereço do rótulo trap_handler
csrw mtvec, t0 # no registrador mtvec

li t0, PLIC_BASE # Habilita Interrupções no PLIC


li t1, -1
sw t1, 0(t0)

li t2, 0x800 # habilita Interrupções Externas Seta os bits 11 (MEIE)


csrs mie, t2 # do registrador mie

li t1, 0x08 # # Habilita Interrupções Global Seta o bit 3 (MIE) # pag 108
csrs mstatus, t1 # do registrador mstatus

ret
18
intr.S
intr.S
main.c
config_intr: configura o processador para realizar o tratamento da interrupção
trap_handler: chamado na interrupção, e por sua vez chama o irq_dispatcher
perif.h e perif.c

trap_handler:
addi sp, sp, -64 intr.S trap.c

sw ra, 60(sp) Hardware


....
// salva registradores na pilha (contexto)
sw t6, 0(sp)

csrr a0, mcause


// except_handler não implementado neste exemplo
bgez a0, except_handler
andi a0, a0, 0x3F // verifica a causa da interrupção

jal irq_dispatcher // chama dispatcher tendo em a0 o valor do mcause


// recupera context
lw t6, 0(sp)
….
lw ra, 60(sp)
addi sp, sp, 64
mret // retornar para o mepc (PC+4 de onde a interrupção foi atendida) 19
trap.c
trap.c: implementa o irq_dispatcher main.c

#define PLIC_IRQ_ID (*((volatile unsigned int*)0x70200004))


#define PLIC_ACK (*((volatile unsigned int*)0x70200004)) perif.h e perif.c
#define MEI 11
#define PERIPH 1
intr.S trap.c
void irq_mei_dispatcher()
{ Hardware
switch(PLIC_IRQ_ID){
case PERIPH:
periph_handler();
PLIC_ACK = PERIPH; // envia iack - fim da interrupção 1: interrupção: trap handler
break;
default:
break; 2: irq_dispatcher
}}

void irq_dispatcher(int cause)


{ 3: irq_mei_dispatcher
switch(cause){
case MEI:
irq_mei_dispatcher(); p.106
break;
4: periph_handler (próxima camada)
default: // codigo para excessão
break;
}} 20
perif.h e perif.c
perif.h
main.c
mapa de memória
protótipo das funções que comunicam-se com o periférico (driver) perif.h e perif.c

intr.S trap.c

#define OPA (*((volatile unsigned int*)0x90000000)) Hardware


#define OPB (*((volatile unsigned int*)0x90000100))
#define RESULT (*((volatile unsigned int*)0x90000200))
#define START (*((volatile unsigned int*)0x90000300))

void periph_handler();
bool get_periph_ready();
void clear_periph_ready();
void set_operand_a(int val);
void set_operand_b(int val);
void periph_start(); API do periférico
void periph_stop();
int get_periph_data();
int read_periph_result();
21
perif.h e perif.c
perif.c
main.c
- Recepção de dados quando a interrupção é ativada - periph_handler
- Funções de semáforo perif.h e perif.c

- Leitura e escrita de registradores mapeados em memória


intr.S trap.c
// Semaphore variables
Hardware
uint32_t periph_data; start=0 iack=0

1
t=
bool periph_ready = false;

ar
st
S_IDLE S_SUM S_INT

1: interrupção: trap handler


iack=1

void periph_handler(){ 2: irq_dispatcher


periph_data = RESULT; // resultado
periph_stop(); // Baixar o start
3: irq_mei_dispatcher
periph_ready = true; // Ativa semáforo
}
4: periph_handler
Observem o sincronismo hw-sw: baixa-se o start, e só depois
(no irq_mei_dispatcher) sobe o iack
22
perif.h e perif.c
perif.c
main.c
- Recepção de dados quando a interrupção é ativada - periph_handler
- Funções de semáforo perif.h e perif.c

- o software (main.c) após enviar dados ao periférico, ficará


aguardando que a interrupção seja tratado intr.S trap.c

- Leitura e escrita de registradores mapeados em memória Hardware

// lê o estado do semáforo
1: interrupção: trap handler
bool get_periph_ready(){
return periph_ready;
} 2: irq_dispatcher

// desativa o semáforo 3: irq_mei_dispatcher


void clear_periph_ready(){
periph_ready = 0;
} 4: periph_handler

23
perif.h e perif.c
perif.c
main.c
- Recepção de dados quando a interrupção é ativada - periph_handler
- Função de semáforo perif.h e perif.c

- Leitura e escrita de registradores mapeados em memória


intr.S trap.c

void set_operand_a(int val){


Hardware
OPA = val; }
1: interrupção: trap handler
void set_operand_b(int val){
OPB = val; }
2: irq_dispatcher
void periph_start(){
START = 1; }
3: irq_mei_dispatcher
void periph_stop(){
START = 0; }
4: periph_handler
int get_periph_data(){
return periph_data; } 24
#include <stdio.h>
#include "../include/periph.h" main.c
void extern config_intr(void);
main.c
int main()
{
int periph_data = 0;
perif.h e perif.c
int a, b = 0;

config_intr(); // Habilitar interrupção intr.S trap.c

for (int i = 0; i < 10; i++) Hardware


{
a = i*3;
b = i*5;
set_operand_a(a);
set_operand_b(b);
clear_periph_ready(); // Limpa o semáforo (false)
periph_start(); // Start no periférico

while(!get_periph_ready()); // Aguarda o semáforo ser verdadeiro (true)

periph_data = get_periph_data(); // Acessa o dado lido pela interrupção

printf("a = %d , b = %d, Result: %d\n", a, b, periph_data);


}

return 0;
}
25
Validação
Baixado os arquivos de [Link] organize os arquivos
e pastas nos locais corretos

(1) Compile a aplicação do periférico para obter o bin:


cd app/peripheral-test/

make clean
Cleaning up

make
Compiling src/trap.c...
Compiling src/periph.c...
Compiling src/main.c...
Compiling ../common/newlib.c...
Assemblying src/intr.S...
Assemblying ../common/crt0.S...
Linking [Link]...
/soft64/util/……..with RWX permissions
Generating [Link]...
Generating [Link]...
26
Validação
(2) completar o test bench conforme slides 5-8

(3) Simulação inicial com verilator


-- RUN ---------------------
a = 0 , b = 0, Result: 0
a = 3 , b = 5, Result: 8
a = 6 , b = 10, Result: 16
a = 9 , b = 15, Result: 24
a = 12 , b = 20, Result: 32
a = 15 , b = 25, Result: 40 10 iterações de soma de (i*3)+(i*5)
a = 18 , b = 30, Result: 48
a = 21 , b = 35, Result: 56
a = 24 , b = 40, Result: 64
a = 27 , b = 45, Result: 72
# 474470 END OF SIMULATION
- [Link]: Verilog $finish
- S i m u l a t i o n R e p o r t: Verilator 5.024 2024-04-05
- Verilator: $finish at 474us; walltime 0.047 s; speed 0.000 s/s
- Verilator: cpu 0.000 s on 1 threads; alloced 217 MB

-- DONE -------------------- 27
Visualizando a simulação da interrupção
Simular no Questa e inserir os sinais do periférico na forma de onda, como abaixo:

Evento 1:
escreve o set_operand_a(a)
primeiro à o en_i e we_i sobem
à o valor de data_i é armazenado em opA
operando 28
Visualizando a simulação da interrupção
Evento 2 – escreve o segundo operando

set_operand_b(b)
à o en_i e we_i sobem
à o valor de data_i armazenado em opB
29
Visualizando a simulação da interrupção
Evento 3 – inicia o periférico, sobe start, calcula o resultado, e sobe o pedido de int

start=0 iack=0

periph_start(); // Start no periférico


1
t=
ar
st

S_IDLE S_SUM S_INT


à sobe o start
à o periférico calcula a soma
iack=1 à sobe a interrupção 30
Visualizando a simulação da interrupção
Eventos 4 e 5 – CPU lê o resultado e faz com que o start vá a zero (periph_stop();)

periph_handler
à periph_data = RESULT;
Observem o tempo para processar a interrupção – uma da à periph_stop();
causas é a necessidade do trap_handler salvar o contexto à periph_ready = true; // semáforo 31
Visualizando a simulação da interrupção
Eventos 6 – recepção do ack

start=0 iack=0

irq_mei_dispatcher
1
t=
ar
st

S_IDLE S_SUM S_INT


case PERIPH:
periph_handler();
PLIC_ACK = PERIPH; // envia iack
iack=1 32
Visualizando a simulação da interrupção
Observar o tempo
entre os eventos do sinal
start (+-48us)

à Software consome
tempo!

- Verilator: $finish at 474us;

Confere com o Verilator!


(10 iterações)

33
Exercício
Periférico com um maior número de endereços mapeados em memória
(usando offsets)

No lugar de enviarmos set_operand_a(a);

Iremos trabalhar com: set_operand_a(a, offset);

34
Produto escalar (dot product)
O produto escalar (dot product) é uma operação matemática que relaciona dois vetores. Seu resultado
é um número escalar, calculado pela soma do produto dos elementos correspondentes dos vetores.

Fórmula Geral:
Seja u = (u1, u2, u3) e v = (v1, v2, v3), então:
Operação de Convolução (CNN)
u ⋅ v = u1×v1 + u2×v2 + u3×v3

• Vetores: u = (1, 2, 3), v = (4, 5, 6) à u ⋅ v = 1×4 + 2×5 + 3×6 = 4 + 10 + 18 = 32

• Vetores: u = (0, 1, 2), v = (2, 0, 1) à u ⋅ v = 0×2 + 1×0 + 2×1 = 0 + 0 + 2 = 2

• Vetores: u = (-1, 0, 3), v = (3, -2, 1) à u ⋅ v = -1×3 + 0×(-2) + 3×1 = -3 + 0 + 3 = 0

• Vetores: u = (2, 2, 2), v = (1, 1, 1) à u ⋅ v = 2×1 + 2×1 + 2×1 = 2 + 2 + 2 = 6

• Vetores: u = (1, -1, 0), v = (0, 3, -2) à u ⋅ v = 1×0 + (-1)×3 + 0×(-2) = 0 - 3 + 0 = -3

35
Software (1/4)
Criar uma nova aplicação a partr da aplicação peripheral-test
cd app
cp -r peripheral-test/ perif2 à opA e opB tem faixa de endereçamento de 255 posições (OK). Alterar
os ponteiros OPA e os protótipos das funções de escrita:

perif2
#define OPA(offset) (*(volatile uint32_t *)(0x90000000 + offset*4))
#define OPB(offset) (*(volatile uint32_t *)(0x90000100 + offset*4))
|-- include void set_operand_a(int val, int offset);
void set_operand_b(int val, int offset);
| `-- periph.h
|-- Makefile
main.c
`-- src
|-- intr.S à não requer alteração – funções de interface com o processador perif.h e perif.c

|-- main.c
|-- periph.c intr.S trap.c

`-- trap.c à não requer alteração – funções de configuração do processador Hardware

36
Software (2/4)
Perif.c: deve agora tratar offsets dos operandos na escrita
(lembrar que não implementamos a leitura)

perif2 //////////////////////////////////////////
|-- include // Read and write in the peripheral
| `-- periph.h /////////////////////////////////////////
|-- Makefile void set_operand_a(int val, int offset){
`-- src OPA(offset) = val;

|-- intr.S }

|-- main.c void set_operand_b(int val, int offset){


|-- periph.c à modificar OPB(offset) = val;
`-- trap.c }

37
Software (3/4)
main.c: deve escrever os valores no periférico e ler o valor do dot product

int main()
{
int periph_data = 0;

int N = 6;
perif2 int u[][3] = {{1,2,3},{0,1,2},{-1,0,3},{2,2,2},{1,-1,0},{-150,75,200}};

|-- include
int v[][3] = {{4,5,6},{2,0,1},{3,-2,1},{1,1,1},{0,3,-2},{100,-50,-75}};

| `-- periph.h
config_intr(); // Habilitar interrupção RS5

|-- Makefile for (int i = 0; i < N; i++)

`-- src {
for(int j=0; j<3; j++){
|-- intr.S printf("(%3d %3d) ", u[i][j], v[i][j] );
set_operand_a(u[i][j], j); // j é o indice do vetor
|-- main.c set_operand_b(v[i][j], j);
}
|-- periph.c clear_periph_ready(); // Limpa o semáforo (false)

`-- trap.c periph_start(); // Start no periférico

… continua (só precisei alterar o printf no final)

38
Software (4/4)
Testar a compilação

make
perif2 Compiling src/trap.c...
|-- include Compiling src/periph.c...
Compiling src/main.c...
| `-- periph.h
Compiling ../common/newlib.c...
|-- Makefile Assemblying src/intr.S...
`-- src Assemblying ../common/crt0.S...
|-- intr.S Linking [Link]...
|-- main.c /soft64/util/……has a LOAD segment with RWX permissions
|-- periph.c Generating [Link]...
`-- trap.c Generating [Link]...

39
Hardware (1/2)
Criar um novo periférico a partir do fornecido (pasta RTL): cp [Link] [Link]

Mudar o nome do perif para periph2: module periph2 (

No testbench:
localparam string BIN_FILE = "../app/perif2/[Link]";

periph2 periph( // alteração a instanciação para usar o novo periférico

No [Link]:
logic [31:0] opA [0:2];
• Usar vetores para opA e opB:
logic [31:0] opB [0:2];

• Ler os valores de opA e opB de acordo com o offset. Exemplo: 12'h004: opA[1] <= data_i;

• Implementar o dot product em result


40
Hardware (2/2)
Testando o hardware com a aplicação:

-- RUN ---------------------
( 1 4) ( 2 5) ( 3 6) Dot Product 0: 32
( 0 2) ( 1 0) ( 2 1) Dot Product 1: 2
( -1 3) ( 0 -2) ( 3 1) Dot Product 2: 0
( 2 1) ( 2 1) ( 2 1) Dot Product 3: 6
( 1 0) ( -1 3) ( 0 -2) Dot Product 4: -3
(-150 100) ( 75 -50) (200 -75) Dot Product 5: -33750

# 675470 END OF SIMULATION


- [Link]: Verilog $finish
- S i m u l a t i o n R e p o r t: Verilator 5.024 2024-04-05
- Verilator: $finish at 675us;

41
Simulação no Questa

set_operand_a(u[i][j], j);
à escritas (último dot product)
42
Simulação no Questa

Pedido de INT Desce start


Start e execução Int ACK
do dot product Desce IRQ (no IDLE)
Terceira escrita

43
Final da aula 4
RS5
44
44

Você também pode gostar