Created
August 26, 2019 21:21
-
-
Save PedroHLC/83b961442cb2fb2166ee5bd711172337 to your computer and use it in GitHub Desktop.
Documentação do Verilog do circuito de Fibonacci com memória síncrona.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Sistemas Digitais - Exercício Prático de Verilog | |
Documentação do Verilog do circuito de Fibonacci com memória síncrona. | |
Por Pedro Henrique Lara Campos | |
Atualizado em 2019-08-26 18:20 | |
==================================================================================== | |
[ BOA LEITURA :D ] | |
1. Módulo da memória | |
Nome: SRAM (Synchronous RAM) | |
Quando falando em memória síncrona é apenas outro termologia para blocante, que por | |
sua vez quer dizer que nessa memória o acesso não pode ser paralelizado, ou seja, | |
escritas e/ou leituras não podem ser simultâneas. | |
Isso implica no uso do símbolo '<=' nas atribuições. | |
Código adaptado do exemplo: https://timetoexplore.net/blog/block-ram-in-verilog-with-vivado | |
*/ | |
module sram | |
/* Note agora, que a linha seguinte não são as entradas e saídas do módulo, mas sim | |
parâmetros de ajustes nas dimensões da nossa SRAM, podemos comparar cada instân- | |
cia desse módulo como pentes de memória em um computador e cada pente tem seu | |
próprio tamanho. | |
Parâmetros: | |
* ADDR_WIDTH: Quantidade em bits do tamanho dos "endereços" de cada dado. | |
O que esse número quer dizer? Imagine uma rua onde os números das casas | |
podem ir de 0 à 99. Os dados na nossa memória como as casas nessa rua | |
são enumerados um a um, na rua chamamos essa enumeração de "número de | |
cada casa", cada uma tem um e apenas um "número". Nas instância de uma | |
SRAM os dados por sua vez também possuem um "número", só que a | |
termologia utilizada é outra e eles passam a chamar "endereços". | |
Como a quantidade de dados acaba sendo limitada pelo número de | |
registradores impressos no circuito, nós temos de elaborar um número | |
binário (então consequentemente potência de 2) que consiga representar | |
cada um dos dadso individualmente, ou seja nosso número vai "endereçar". | |
Na rua hipotética esse o endereço era formado por dois decimais (00 à | |
99). Consequentemente só poderiam-se ter 100 casas diferentes. | |
O exercício põem como um valor padrão para os endereos 8 bits (000 à | |
255). Consequentemente podem-se ter 256 dados diferentes. | |
* DATA_WIDTH: Tamanho em bits de cada dado, ou seja, todos os dados inseri- | |
dos na memória ficam limitados a esse teto. | |
Um tamanho de 32 bits significa que todos dados teram de ser representa- | |
dos em números de 0 à (2³² - 1), ou seja de 0 à 4.294.967.295. | |
* DEPTH: Quantidade de dados impressos no circuito da instância da SRAM. | |
Retornando ao exemplo hipotétito apresentado em ADDR_WIDTH, esse número | |
representaria a quantidade de casas efetivamente na nossa rua. | |
Ele pode ser diferente da quantidade limite de "endereços" definido por | |
ADDR_WIDTH, mas isso significa que será necessário tratar essa diferença | |
e isso não é tão simples. | |
O exercício traz o número 256 que casa perfeito com os 8 bits (00 à 255) | |
definido como padrão para ADDR_WIDTH, então todo dado tem um endereço e, | |
todo endereço um dado. | |
*/ | |
#(parameter ADDR_WIDTH = 8, DATA_WIDTH = 32, DEPTH = 256) | |
/* Agora para o bloco formado pelos parênteses a seguir temos as conexões de fios | |
que servem de interface para comunicação dos outros componentes com cada instân- | |
cia dessa SRAM. | |
Conexões (Como usar/instanciar esse módulo): | |
i_clk: Entrada com o sinal Clock. | |
i_addr: Entrada com o endereço que é para ser lido/inserido/substituido. | |
i_write: Entrada com a operação. Se ALTO (binário 1) então o valor em i_data | |
será inserido/substituirá o dado na SRAM de endereço i_addr. Se BAIXO | |
(binário 0) o dado na SRAM de endereço i_addr será armazenado no regis- | |
trador conectado à o_data. | |
i_data: Entrada com um novo dado. (veja i_write). | |
o_data: Registrador e saída com novo dado (veja i_write). | |
*/ | |
( | |
input wire i_clk, | |
input wire [ADDR_WIDTH-1:0] i_addr, | |
input wire i_write, | |
input wire [DATA_WIDTH-1:0] i_data, | |
output reg [DATA_WIDTH-1:0] o_data | |
); | |
/* Todos registradores juntos que formam a memória, armazenando todos os dados dessa | |
instância de SRAM. | |
É nessa linha e apenas nesas linha que o Verilog descreve todos os registradores | |
que compõem a memória. | |
NOTE os colchetes em ambos os lados: | |
reg [A:A'] nome [B:B']; | |
Em A temos a quantidade de bits (assim como sua ordenação e intervalo) de | |
cada registrador e em B temos a quantidade unitária de registradores na me- | |
mória. | |
*/ | |
reg [DATA_WIDTH-1:0] memory_array [0:DEPTH-1]; | |
/* No bloco (begin..end) seguinte temos a operação lógica que é realizada sempre | |
que a entrada i_clk sobe nessa instância. Resumidamente: | |
Quando clock sobe: | |
Se i_write é ALTO (binário 1) então o valor em i_data será inserido/ | |
substituirá o dado na SRAM de endereço i_addr. | |
Se i_write é BAIXO (binário 0) o dado na SRAM de endereço i_addr será | |
armazenado no registrador conectado à o_data. | |
*/ | |
always @ (posedge i_clk) | |
begin | |
// Se i_write é ALTO | |
if(i_write) begin | |
// armazena i_data no registrador de "endereço i_addr" | |
memory_array[i_addr] <= i_data; | |
end | |
// Caso contrário | |
else begin | |
// armazena em o_data a saída do registrador de "endereço i_addr" | |
o_data <= memory_array[i_addr]; | |
end | |
end | |
endmodule | |
/* 2. Módulo que calcula Fibonacci do número seguinte | |
Esse módulo calcula a sequência Fibonnaci ao invés de um número utilizando recursi- | |
vidade. Em outras palavras depois de um reset a cada clock ele responderá um número | |
diferente da sequência Fibonnaci (sem fugir da sequência). | |
clock=rst=1 => fibo=0; | |
após 1º clock => fibo=1; | |
após outro clock => fibo=2; ... | |
*/ | |
module fibonacci | |
/* Conexões: | |
clk: Entrada com o sinal Clock. | |
rst: Entrada com o sinal que reinicia corretamente o circuito. | |
fibo: Saída com o próximo número da sequência. | |
*/ | |
(clk, rst, fibo); | |
input clk, rst; | |
output [31:0] fibo; | |
// Registradores que armazenam os últimos dois números computados da sequência. | |
reg [31:0] a, b; | |
/* No bloco (begin..end) seguinte temos a operação lógica que é realizada sempre | |
que a entrada clk sobe nessa instância. Resumidamente, quando o clock sobe: | |
Se o reset estiver ALTO (binário 1). Então os registradores A e B recebem o iní- | |
cio da sequência, ou seja, respecitivamente os números { 0, 1 }. | |
Se o reset estiver BAIXO (binário 0). Então o número seguinte é calculado, se- | |
guindo a definição de Fibonnaci (um número na sequencia é sempre a soma dos | |
dois anteriores), o número que estava em B é passado para A, o novo é armazenado | |
agora em B. | |
*/ | |
always@(posedge clk, posedge rst) | |
if (rst) begin | |
a <= 0; | |
b <= 1; | |
end | |
else begin | |
a <= b; | |
b <= a + b; | |
end | |
/* Note que o número que é retornado na nossa saída não é o novo calculado e sim o | |
que agora esta armazenado em A. Isso é o comportamento esperado já que a sequên- | |
cia recebido pelo módulo que está instanciando esse espera que os números 0 e | |
1 estejam na sequência, formando {0, 1, 1, 2, 3, 5, 8, ...} | |
*/ | |
assign fibo = a; | |
endmodule | |
/* 3. Módulo que conecta as instância formando o circuito desejado | |
Esse módulo representa na verdade o circuito que desejamos implementar/imprimir. | |
Ele conecta os demais módulos criando as instâncias, ligando os fios, conectando as | |
entradas e saídas até obter o comportamento esperado. | |
Nesse caso o compartamento esperado é armazenar 47 números da sequência Fibonacci | |
em uma memória SRAM (de parâmetros padrões), e posteriormente ler os mesmos. | |
*/ | |
module fibo_ram(clk, rst, i_fibo, o_fibo); | |
/* Conexões: | |
clk: Entrada com o sinal Clock. | |
rst: Entrada com o sinal que reinicia corretamente o circuito. | |
i_fibo: Saída com número que está sendo escrito na memória. | |
o_fibo: Saída com número que está sendo lido da memória. | |
*/ | |
input clk, rst; | |
output [31:0] i_fibo, o_fibo; | |
// Registrador do endereço que estamos trabalhando no moment | |
reg [7:0] addr; | |
// Registrador com o bit da operação (ALTO escreve i_fibo, BAIXO lê para o_fibo) | |
reg we; | |
// Nossas instância, já fazendo as conexões | |
fibonacci f(clk, rst, i_fibo); | |
sram m(clk, addr, we, i_fibo, o_fibo); | |
/* Esse bloco lógico if...else...end é executado para toda vez que o clk sobe. | |
Quando não em estado de reset a cada clock um endereço de memória diferente é | |
percorrido, primeiro escrevendo os números obtidos pelo modulo de fibonnaci | |
(presente em i_fibo) para o "endereço addr" até atingir a condição "addr > 46", | |
então o registrador we é mudado e o compartamento passa a ser diferente, e agora | |
a cada clock números são lidos da memória vindo do "endereço addr" (que é reini- | |
ciado junto com a mudança em we) e armazenados em o_fibo. | |
*/ | |
always@(posedge clk, posedge rst) | |
if (rst) begin | |
/* O estado de reset coloca o endereço em 0 (primeiro endereço) e coloca o | |
comportamento para ser de escrita (i_fibo é escrito em addr). | |
*/ | |
addr <= 0; | |
we <= 1; | |
end | |
else begin | |
// Avançamos um endereço cada vez que o clock sobe | |
addr <= addr + 1; | |
/* Se o compartamento for de escrita e estivermos no endereço 47, mudamos de | |
comportamento | |
*/ | |
if (we && addr > 46) begin | |
addr <= 0; | |
we <= 0; | |
end | |
end | |
endmodule |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment