Skip to content

Commit

Permalink
Adicionar implementação do protocolo onewire usando VHDL (#66)
Browse files Browse the repository at this point in the history
* Create README.md

* Adicionar arquivos do protocolo onewire

* Adicionar figuras com diagrama de intervalo de escrita e leitura

* Atualizar documentação do projeto

* Corrigir data em docstring de tb.do
  • Loading branch information
ericreis27 authored Dec 19, 2023
1 parent bbe7e9c commit 284ea12
Show file tree
Hide file tree
Showing 10 changed files with 414 additions and 0 deletions.
39 changes: 39 additions & 0 deletions peripherals/onewire/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Protocolo One-wire

Este projeto visa a implementação do protocolo de comunicação one-wire utilizando VHDL.

O protocolo one-wire é um padrão de comunicação serial utilizado para conectar dispositivos eletrônicos de forma que possam ser controlados e alimentados por um único fio de dados, além de possuírem identificação única. Ele é comumente usado em sensores de temperatura, memórias EEPROM e outros dispositivos eletrônicos de baixo custo e baixo consumo de energia. O protocolo permite que múltiplos dispositivos sejam conectados ao mesmo barramento de comunicação.

A temporação utilizada neste projeto foi baseada na documentação do sensor DS18B20, um sensor de temperatura de baixo custo que utiliza o protocolo one-wire para comunicação

Para implementarmos esse protocolo foi criada a seguinte máquina de estados, representada na figura abaixo.

![](https://raw.githubusercontent.com/ericreis27/riscv-multicycle/master/peripherals/onewire/figures/diagram_onewire.png)

O protocolo one-wire segue temporizações rigorosas para que os dados possam ser transmitidos de forma correta. Abaixo iremos descrever o funcionamento do protocolo em paralelo com a máquina de estados criada.

### Procedimento de inicialização

No protocolo OneWire, a inicialização começa quando o mestre (o dispositivo controlador) envia um pulso de reset para a linha de dados. Durante a inicialização, o mestre envia um pulso de reset por um determinado periodo de tempo e espera a resposta de algum dispositivo no barramento de dados, esse processo ocorre entre os estados __RESET, SAMPLE DEVICE RESPONSE e DEVICE RECOVERY RESPONSE__. Caso dentro de uma janela específica de tempo algum dispositivo não puxe o barramento de dados para o GND sinalizando sua presença, a máquina de estados volta para o estado IDLE e reinicia o processo de inicialização buscando novamente por um dispositivo. Caso obtenha a resposta de algum dispositivo, a máquina prossegue para a fase de escrita no barramento. A figura a seguir demonstra a temporização e como funciona esse procedimento.

![](https://raw.githubusercontent.com/ericreis27/riscv-multicycle/master/peripherals/onewire/figures/initialization_onewire.png)

### WRITE
Durante a escrita, desejamos enviar algum comando para que o dispositivo nos responda com os dados requisitados. Esse envio é feito seguindo um protocolo restrito de temporização para o envio de cada bit do comando. Abaixo segue as especificações de temporização utilizadas para o envio de um bit.

![](https://raw.githubusercontent.com/ericreis27/riscv-multicycle/master/peripherals/onewire/figures/write_time_slot.png)

O envio dos bits é feito entre os estados __MASTER WRITE, MASTER WRITE SLOT E MASTER WRITE RECOVERY__, onde será iterado em cada um dos bits do comando de escrita e serão enviados para o dispositivo. Após o envio do comando entramos num estado __MASTER POST WRITE DELAY__ antes de iniciarmos o processo de leitura.

### READ
Da mesma forma que a escrita, a leitura da resposta do dispositivo segue temporização rigorosa. Abaixo seguem dois diagramas detalhando melhor as especificações utilizadas durante o processo de leitura.

![](https://raw.githubusercontent.com/ericreis27/riscv-multicycle/master/peripherals/onewire/figures/read_time_slot.png)

![](https://raw.githubusercontent.com/ericreis27/riscv-multicycle/master/peripherals/onewire/figures/recommended_read_timings.png)

Durante o processo de escrita iremos ciclar entre os estados __MASTER READ, MASTER AWAIT READ SAMPLE E MASTER READ RECOVERY__, atuando de forma cíclica extremamente similar a como fazemos o processo de escrita, porém trabalhando com temporizações diferentes. Após a leitura de todos os bits de resposta do dispositivo finalmente vamos para um estado de recuperação __MASTER POST READ DELAY__ e finalmente chegamos no fim da implementação do protocolo no estado __DONE__.

### ToDo
Atualmente foi implementado o envio e leitura de dados utilizando a temporização adequada do protocolo, porém demonstrando a leitura e escrita utilizando valores **hardcoded**. A adequação do projeto para que ele possa enviar qualquer tipo de comando e também tratar os dados adequadamente seria o próximo passo importante neste projeto

Binary file added peripherals/onewire/figures/diagram_onewire.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added peripherals/onewire/figures/read_time_slot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added peripherals/onewire/figures/write_time_slot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
272 changes: 272 additions & 0 deletions peripherals/onewire/onewire.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
-------------------------------------------------------
--! file: onewire.vhd
--! author: Eric Monteiro dos Reis
--! Description: OneWire Protocol implementation following the timing specifications for the DS18B20 sensor available at:
--! https://www.analog.com/media/en/technical-documentation/data-sheets/ds18b20.pdf
--! For more details about how the onewire protocol was implemented here, access: https://github.com/ericreis27/vhdl_onewire
-------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;


entity onewire is
-- all generic values below represent a micro-seconds time value.
generic (
RESET_PULSE_TIME : positive := 500;
SAMPLE_RESPONSE_TIME : positive := 100;
DEVICE_RESPONSE_RECOVERY_TIME : positive := 400;
SEND_DATA_WAIT_TIME: positive := 500;
MASTER_WRITE_TIME : positive := 10;
MASTER_WRITE_SLOT_TIME : positive := 50;
MASTER_WRITE_RECOVERY_TIME : positive := 5;
MASTER_POST_WRITE_DELAY_TIME : positive := 480;
MASTER_AWAIT_READ_SAMPLE_TIME : positive := 10;
MASTER_READ_RECOVERY_TIME: positive := 55;
MASTER_POST_READ_DELAY_TIME : positive := 480
);

port (
clk : in std_logic;
rst : in std_logic;
data_bus : inout std_logic
);
end entity onewire;

architecture rtl of onewire is
type state_type is (
IDLE,
RESET,
SAMPLE_DEVICE_RESPONSE,
DEVICE_RECOVERY_RESPONSE,
MASTER_WRITE_DELAY,
MASTER_WRITE,
MASTER_WRITE_SLOT,
MASTER_WRITE_RECOVERY,
MASTER_POST_WRITE_DELAY,
MASTER_READ,
MASTER_AWAIT_READ_SAMPLE,
MASTER_READ_RECOVERY,
MASTER_POST_READ_DELAY,
DONE
);
signal all_zeros : unsigned(31 downto 0) := (others => '0');
signal state : state_type;
signal tristate : std_logic; -- this is used to control the inout data_bus
signal iterator : unsigned(2 downto 0) := "000";
signal counter_clk : std_logic;
signal time_slot : unsigned(31 downto 0);
begin
data_bus <= '0' when tristate = '0' else 'Z';
state_transition : process (counter_clk, rst) is
variable device_response_flag : std_logic := '1';
begin
if rst = '1' then
state <= IDLE;

elsif rising_edge(counter_clk) then
case state is
when IDLE =>
state <= RESET;
when RESET =>
state <= SAMPLE_DEVICE_RESPONSE;
when SAMPLE_DEVICE_RESPONSE =>
if (data_bus = '0') then
device_response_flag := data_bus;
else
device_response_flag := '1'; --this is done so the device_response_flag doesn't have a tristate value due to the data_bus.
end if;

state <= DEVICE_RECOVERY_RESPONSE;
when DEVICE_RECOVERY_RESPONSE =>
if (device_response_flag = '0') then
state <= MASTER_WRITE_DELAY;
else
state <= IDLE;
end if;
when MASTER_WRITE_DELAY =>
state <= MASTER_WRITE;
when MASTER_WRITE =>
state <= MASTER_WRITE_SLOT;
when MASTER_WRITE_SLOT =>
state <= MASTER_WRITE_RECOVERY;

when MASTER_WRITE_RECOVERY =>
if (iterator = 7) then
state <= MASTER_POST_WRITE_DELAY;
iterator <= (others => '0');
else
iterator <= iterator + 1;
state <= MASTER_WRITE;
end if;
when MASTER_POST_WRITE_DELAY =>
state <= MASTER_READ;
when MASTER_READ =>
state <= MASTER_AWAIT_READ_SAMPLE;
when MASTER_AWAIT_READ_SAMPLE =>
state <= MASTER_READ_RECOVERY;
when MASTER_READ_RECOVERY =>
if (iterator = 7) then
state <= MASTER_POST_READ_DELAY;
iterator <= (others => '0');
else
iterator <= iterator + 1;
state <= MASTER_READ;
end if;
when MASTER_POST_READ_DELAY =>
state <= DONE;
when DONE =>
null;
end case;
end if;
end process state_transition;

state_machine_control : process (state, iterator) is
variable write_command : std_logic_vector(7 downto 0) := "00110011"; -- command value to be sent for test purposes
variable data_buffer : std_logic_vector(7 downto 0) := "00000000"; -- buffer to be used to save the incoming values while reading from the device
begin
tristate <= '1';
case state is
when IDLE =>
null;
when RESET =>
tristate <= '0';
when SAMPLE_DEVICE_RESPONSE =>
null;
when DEVICE_RECOVERY_RESPONSE =>
null;
when MASTER_WRITE_DELAY =>
null;
when MASTER_WRITE =>
tristate <= '0';
when MASTER_WRITE_SLOT =>
if (write_command(to_integer(iterator)) = '1') then
tristate <= '1';
else
tristate <= '0';
end if;
when MASTER_WRITE_RECOVERY =>
tristate <= '1';
when MASTER_POST_WRITE_DELAY =>
null;
when MASTER_READ =>
tristate <= '0';
when MASTER_AWAIT_READ_SAMPLE =>
tristate <= '1';
when MASTER_READ_RECOVERY =>
if (data_bus = '0') then
data_buffer(to_integer(iterator)) := '0';
else
data_buffer(to_integer(iterator)) := '1';
end if;
when MASTER_POST_READ_DELAY =>
null;
when DONE =>
null;
end case;
end process state_machine_control;

counter : process (clk, rst) is
begin
if rst = '1' then
time_slot <= to_unsigned(1, time_slot'length);
elsif rising_edge(clk) then
case state is
when IDLE =>
if (time_slot > 0) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;
when RESET =>
if (time_slot >= RESET_PULSE_TIME - 1) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;
when SAMPLE_DEVICE_RESPONSE =>
if (time_slot >= SAMPLE_RESPONSE_TIME - 1) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;
when DEVICE_RECOVERY_RESPONSE =>
if (time_slot >= DEVICE_RESPONSE_RECOVERY_TIME - 1) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;
when MASTER_WRITE_DELAY =>
if (time_slot >= SEND_DATA_WAIT_TIME - 1) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;

when MASTER_WRITE =>
if (time_slot >= MASTER_WRITE_TIME - 1) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;
when MASTER_WRITE_SLOT =>
if (time_slot >= MASTER_WRITE_SLOT_TIME - 1) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;
when MASTER_WRITE_RECOVERY =>
if (time_slot >= MASTER_WRITE_RECOVERY_TIME - 1) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;
when MASTER_POST_WRITE_DELAY =>
if (time_slot >= MASTER_POST_WRITE_DELAY_TIME - 1) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;
when MASTER_READ =>
if (time_slot > 0) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;
when MASTER_AWAIT_READ_SAMPLE =>
if (time_slot >= MASTER_AWAIT_READ_SAMPLE_TIME - 1) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;
when MASTER_READ_RECOVERY =>
if (time_slot >= MASTER_READ_RECOVERY_TIME - 1) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;
when MASTER_POST_READ_DELAY =>
if (time_slot >= MASTER_POST_READ_DELAY_TIME - 1) then
time_slot <= (others => '0');
else
time_slot <= time_slot + 1;
end if;
when DONE =>
null;
end case;
end if;
end process counter;

-- process responsible to make the state machine sensible to time_slot reaching a specific time value depending on the current state
counter_clk_cycle : process (time_slot)
begin
if time_slot = all_zeros then
counter_clk <= '1';
else
counter_clk <= '0';
end if;
end process;

end architecture rtl;

35 changes: 35 additions & 0 deletions peripherals/onewire/tb.do
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#-------------------------------------------------------------------
#-- File: tb.do
#-- Author : Eric M. dos Reis
#-- Date : 15 de dez. de 2023
#-------------------------------------------------------------------

#Cria biblioteca do projeto
vlib work

#compila projeto: todos os arquivos. Ordem é importante
vcom -93 onewire.vhd testbench.vhd

#Simula (work é o diretorio, testbench é o nome da entity)
vsim -voptargs="+acc" -t ns work.testbench

#Mosta forma de onda
view wave

#Adiciona ondas específicas
# -radix: binary, hex, dec
# -label: nome da forma de onda
add wave -label "clk" -radix binary /clk
add wave -label "Clock" -radix binary /dut/counter_clk
add wave -label "Reset" -radix binary /rst
add wave -label "Data" -radix binary /data_bus
add wave -label "State" -radix symbolic /dut/state
add wave -label "recovery_flag" -radix binary /dut/state_transition/device_response_flag
add wave -label "data_buffer" -radix binary /dut/state_machine_control/data_buffer
add wave -label "iterator" -radix binary /dut/iterator

#Simula até 6000us
run 6000us

wave zoomfull
write wave wave.ps
Loading

0 comments on commit 284ea12

Please sign in to comment.