From 3d81cfe9c1028ae989f580e42aad0414081b5e7c Mon Sep 17 00:00:00 2001 From: Benjamin Chausse Date: Sun, 18 May 2025 14:07:21 -0400 Subject: Batman --- .../sources_1/imports/new/i2c_master.vhd | 247 +++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 pb_logique_seq.srcs/sources_1/imports/new/i2c_master.vhd (limited to 'pb_logique_seq.srcs/sources_1/imports/new/i2c_master.vhd') diff --git a/pb_logique_seq.srcs/sources_1/imports/new/i2c_master.vhd b/pb_logique_seq.srcs/sources_1/imports/new/i2c_master.vhd new file mode 100644 index 0000000..a4fe165 --- /dev/null +++ b/pb_logique_seq.srcs/sources_1/imports/new/i2c_master.vhd @@ -0,0 +1,247 @@ +-------------------------------------------------------------------------------- +-- +-- FileName: i2c_master.vhd +-- Dependencies: none +-- Design Software: Quartus II 64-bit Version 13.1 Build 162 SJ Full Version +-- +-- HDL CODE IS PROVIDED "AS IS." DIGI-KEY EXPRESSLY DISCLAIMS ANY +-- WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING BUT NOT +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +-- PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL DIGI-KEY +-- BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL +-- DAMAGES, LOST PROFITS OR LOST DATA, HARM TO YOUR EQUIPMENT, COST OF +-- PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS +-- BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), +-- ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER SIMILAR COSTS. +-- +-- Version History +-- Version 1.0 11/01/2012 Scott Larson +-- Initial Public Release +-- Version 2.0 06/20/2014 Scott Larson +-- Added ability to interface with different slaves in the same transaction +-- Corrected ack_error bug where ack_error went 'Z' instead of '1' on error +-- Corrected timing of when ack_error signal clears +-- Version 2.1 10/21/2014 Scott Larson +-- Replaced gated clock with clock enable +-- Adjusted timing of SCL during start and stop conditions +-- Version 2.2 02/05/2015 Scott Larson +-- Corrected small SDA glitch introduced in version 2.1 +-- +-------------------------------------------------------------------------------- + +LIBRARY ieee; +USE ieee.std_logic_1164.all; +USE ieee.std_logic_unsigned.all; + +ENTITY i2c_master IS + GENERIC( + input_clk : INTEGER := 50_000_000; --input clock speed from user logic in Hz + bus_clk : INTEGER := 400_000); --speed the i2c bus (scl) will run at in Hz + PORT( + clk : IN STD_LOGIC; --system clock + reset_n : IN STD_LOGIC; --active low reset + ena : IN STD_LOGIC; --latch in command + addr : IN STD_LOGIC_VECTOR(6 DOWNTO 0); --address of target slave + rw : IN STD_LOGIC; --'0' is write, '1' is read + data_wr : IN STD_LOGIC_VECTOR(7 DOWNTO 0); --data to write to slave + busy : OUT STD_LOGIC; --indicates transaction in progress + data_rd : OUT STD_LOGIC_VECTOR(7 DOWNTO 0); --data read from slave + ack_error : BUFFER STD_LOGIC; --flag if improper acknowledge from slave + sda : INOUT STD_LOGIC; --serial data output of i2c bus + scl : INOUT STD_LOGIC); --serial clock output of i2c bus +END i2c_master; + +ARCHITECTURE logic OF i2c_master IS + CONSTANT divider : INTEGER := (input_clk/bus_clk)/4; --number of clocks in 1/4 cycle of scl + TYPE machine IS(ready, start, command, slv_ack1, wr, rd, slv_ack2, mstr_ack, stop); --needed states + SIGNAL state : machine; --state machine + SIGNAL data_clk : STD_LOGIC; --data clock for sda + SIGNAL data_clk_prev : STD_LOGIC; --data clock during previous system clock + SIGNAL scl_clk : STD_LOGIC; --constantly running internal scl + SIGNAL scl_ena : STD_LOGIC := '0'; --enables internal scl to output + SIGNAL sda_int : STD_LOGIC := '1'; --internal sda + SIGNAL sda_ena_n : STD_LOGIC; --enables internal sda to output + SIGNAL addr_rw : STD_LOGIC_VECTOR(7 DOWNTO 0); --latched in address and read/write + SIGNAL data_tx : STD_LOGIC_VECTOR(7 DOWNTO 0); --latched in data to write to slave + SIGNAL data_rx : STD_LOGIC_VECTOR(7 DOWNTO 0); --data received from slave + SIGNAL bit_cnt : INTEGER RANGE 0 TO 7 := 7; --tracks bit number in transaction + SIGNAL stretch : STD_LOGIC := '0'; --identifies if slave is stretching scl +BEGIN + + --generate the timing for the bus clock (scl_clk) and the data clock (data_clk) + PROCESS(clk, reset_n) + VARIABLE count : INTEGER RANGE 0 TO divider*4; --timing for clock generation + BEGIN + IF(reset_n = '0') THEN --reset asserted + stretch <= '0'; + count := 0; + ELSIF(clk'EVENT AND clk = '1') THEN + data_clk_prev <= data_clk; --store previous value of data clock + IF(count = divider*4-1) THEN --end of timing cycle + count := 0; --reset timer + ELSIF(stretch = '0') THEN --clock stretching from slave not detected + count := count + 1; --continue clock generation timing + END IF; + CASE count IS + WHEN 0 TO divider-1 => --first 1/4 cycle of clocking + scl_clk <= '0'; + data_clk <= '0'; + WHEN divider TO divider*2-1 => --second 1/4 cycle of clocking + scl_clk <= '0'; + data_clk <= '1'; + WHEN divider*2 TO divider*3-1 => --third 1/4 cycle of clocking + scl_clk <= '1'; --release scl + IF(scl = '0') THEN --detect if slave is stretching clock + stretch <= '1'; + ELSE + stretch <= '0'; + END IF; + data_clk <= '1'; + WHEN OTHERS => --last 1/4 cycle of clocking + scl_clk <= '1'; + data_clk <= '0'; + END CASE; + END IF; + END PROCESS; + + --state machine and writing to sda during scl low (data_clk rising edge) + PROCESS(clk, reset_n) + BEGIN + IF(reset_n = '0') THEN --reset asserted + state <= ready; --return to initial state + busy <= '1'; --indicate not available + scl_ena <= '0'; --sets scl high impedance + sda_int <= '1'; --sets sda high impedance + ack_error <= '0'; --clear acknowledge error flag + bit_cnt <= 7; --restarts data bit counter + data_rd <= "00000000"; --clear data read port + ELSIF(clk'EVENT AND clk = '1') THEN + IF(data_clk = '1' AND data_clk_prev = '0') THEN --data clock rising edge + CASE state IS + WHEN ready => --idle state + IF(ena = '1') THEN --transaction requested + busy <= '1'; --flag busy + addr_rw <= addr & rw; --collect requested slave address and command + data_tx <= data_wr; --collect requested data to write + state <= start; --go to start bit + ELSE --remain idle + busy <= '0'; --unflag busy + state <= ready; --remain idle + END IF; + WHEN start => --start bit of transaction + busy <= '1'; --resume busy if continuous mode + sda_int <= addr_rw(bit_cnt); --set first address bit to bus + state <= command; --go to command + WHEN command => --address and command byte of transaction + IF(bit_cnt = 0) THEN --command transmit finished + sda_int <= '1'; --release sda for slave acknowledge + bit_cnt <= 7; --reset bit counter for "byte" states + state <= slv_ack1; --go to slave acknowledge (command) + ELSE --next clock cycle of command state + bit_cnt <= bit_cnt - 1; --keep track of transaction bits + sda_int <= addr_rw(bit_cnt-1); --write address/command bit to bus + state <= command; --continue with command + END IF; + WHEN slv_ack1 => --slave acknowledge bit (command) + IF(addr_rw(0) = '0') THEN --write command + sda_int <= data_tx(bit_cnt); --write first bit of data + state <= wr; --go to write byte + ELSE --read command + sda_int <= '1'; --release sda from incoming data + state <= rd; --go to read byte + END IF; + WHEN wr => --write byte of transaction + busy <= '1'; --resume busy if continuous mode + IF(bit_cnt = 0) THEN --write byte transmit finished + sda_int <= '1'; --release sda for slave acknowledge + bit_cnt <= 7; --reset bit counter for "byte" states + state <= slv_ack2; --go to slave acknowledge (write) + ELSE --next clock cycle of write state + bit_cnt <= bit_cnt - 1; --keep track of transaction bits + sda_int <= data_tx(bit_cnt-1); --write next bit to bus + state <= wr; --continue writing + END IF; + WHEN rd => --read byte of transaction + busy <= '1'; --resume busy if continuous mode + IF(bit_cnt = 0) THEN --read byte receive finished + IF(ena = '1' AND addr_rw = addr & rw) THEN --continuing with another read at same address + sda_int <= '0'; --acknowledge the byte has been received + ELSE --stopping or continuing with a write + sda_int <= '1'; --send a no-acknowledge (before stop or repeated start) + END IF; + bit_cnt <= 7; --reset bit counter for "byte" states + data_rd <= data_rx; --output received data + state <= mstr_ack; --go to master acknowledge + ELSE --next clock cycle of read state + bit_cnt <= bit_cnt - 1; --keep track of transaction bits + state <= rd; --continue reading + END IF; + WHEN slv_ack2 => --slave acknowledge bit (write) + IF(ena = '1') THEN --continue transaction + busy <= '0'; --continue is accepted + addr_rw <= addr & rw; --collect requested slave address and command + data_tx <= data_wr; --collect requested data to write + IF(addr_rw = addr & rw) THEN --continue transaction with another write + sda_int <= data_wr(bit_cnt); --write first bit of data + state <= wr; --go to write byte + ELSE --continue transaction with a read or new slave + state <= start; --go to repeated start + END IF; + ELSE --complete transaction + state <= stop; --go to stop bit + END IF; + WHEN mstr_ack => --master acknowledge bit after a read + IF(ena = '1') THEN --continue transaction + busy <= '0'; --continue is accepted and data received is available on bus + addr_rw <= addr & rw; --collect requested slave address and command + data_tx <= data_wr; --collect requested data to write + IF(addr_rw = addr & rw) THEN --continue transaction with another read + sda_int <= '1'; --release sda from incoming data + state <= rd; --go to read byte + ELSE --continue transaction with a write or new slave + state <= start; --repeated start + END IF; + ELSE --complete transaction + state <= stop; --go to stop bit + END IF; + WHEN stop => --stop bit of transaction + busy <= '0'; --unflag busy + state <= ready; --go to idle state + END CASE; + ELSIF(data_clk = '0' AND data_clk_prev = '1') THEN --data clock falling edge + CASE state IS + WHEN start => + IF(scl_ena = '0') THEN --starting new transaction + scl_ena <= '1'; --enable scl output + ack_error <= '0'; --reset acknowledge error output + END IF; + WHEN slv_ack1 => --receiving slave acknowledge (command) + IF(sda /= '0' OR ack_error = '1') THEN --no-acknowledge or previous no-acknowledge + ack_error <= '1'; --set error output if no-acknowledge + END IF; + WHEN rd => --receiving slave data + data_rx(bit_cnt) <= sda; --receive current slave data bit + WHEN slv_ack2 => --receiving slave acknowledge (write) + IF(sda /= '0' OR ack_error = '1') THEN --no-acknowledge or previous no-acknowledge + ack_error <= '1'; --set error output if no-acknowledge + END IF; + WHEN stop => + scl_ena <= '0'; --disable scl + WHEN OTHERS => + NULL; + END CASE; + END IF; + END IF; + END PROCESS; + + --set sda output + WITH state SELECT + sda_ena_n <= data_clk_prev WHEN start, --generate start condition + NOT data_clk_prev WHEN stop, --generate stop condition + sda_int WHEN OTHERS; --set to internal sda signal + + --set scl and sda outputs + scl <= '0' WHEN (scl_ena = '1' AND scl_clk = '0') ELSE 'Z'; + sda <= '0' WHEN sda_ena_n = '0' ELSE 'Z'; + +END logic; -- cgit v1.2.3