cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Highlighted
Visitor
Visitor
124 Views
Registered: ‎07-28-2020

8051 Soft Core To I2C Top Module / Test Bench ONE output signal not working

Just got my Spartan-6 board 3 weeks ago, FPGA's are brand new to me, never touched them before. I configured the Oregano free 8051 soft core and it works perfect on my Xilinx board. It took me 2 solid weeks to make the 8051 work with NO documentation from Oregano, it just doesn't exist, but I know the 8051 real good so that helped. Then I downloaded the Digi Key I2C VHDL code and made it the top module and made my 8051 the other modules because I figured my 8051 was now 'user logic'. All the logic is clocked by one clock. I got the 8051 soft-core to now send out parallel  I2C 7 bit starting address, the R/W bit, the 8 bit data, the enable line and a line in for 'busy' status detection from the Digi Key logic. All of this works perfectly, I see the I2C data perfectly structured on my Saleae logic analyzer when this is all running but for some reason I don't have the 'busy' bit being sent to my 8051 as a 0 (meaning all clear from the top module logic). The 8051 detects it as high all the time = busy. So I busted out the test bench thing and took a few YouTube lessons, but all I can find on there was people showing me how to scope a simple clock signal or a simple AND gate. Maybe I'm approaching this venture all wrong although I'm quite surprised how far I've gotten already. The short of it is, why does my 'busy' signal from the Digi Key I2C top module logic stay high no matter what? Can I not run this complete module through the test bench? Even if the entire code is not do able on test bench, shouldn't I at least see the busy bit signal go low after the reset finishes? Test bench is showing me the same exact thing my 8051 says, and that is the busy bit never changes from high to low.BusyBit.JPG

0 Kudos
1 Reply
Highlighted
Visitor
Visitor
121 Views
Registered: ‎07-28-2020

The code sure would help, sorry about that.

 

 

DigiKey I2C top module

 

 

-
--------------------------------------------------------------------------------

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;

ENTITY i2c_master IS
GENERIC(
input_clk : INTEGER := 12_000_000; --input clock speed from user logic in Hz
bus_clk : INTEGER := 75_000); --speed the i2c bus (scl) will run at in Hz



PORT(

clkMN : IN STD_LOGIC; --system clock
reset2 : 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
resume : IN STD_LOGIC;
busy : OUT STD_LOGIC ; --indicates transaction in progress
data_rd : OUT STD_LOGIC_VECTOR(7 DOWNTO 0); --data read 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

SIGNAL clk12 : std_logic; -- a 12 MHZ clock
SIGNAL count: integer:=1;
SIGNAL tmp : std_logic := '0';

CONSTANT divider : INTEGER := 40; --:= (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, martha, yellow, 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



-- architecture structural
BEGIN




--------------- Clock Divider 50MHZ to 12MHZ -----------------------------

clock_divide_process: process(clkMN,reset2) begin
if(reset2='0') then
count<=1;
tmp<='0';
elsif(clkMN'event and clkMN='1') then
count <=count+1;
if (count = 12) then -- Count of 12 gives 12Mhz from 50Mhz 25 = 1Mhz
tmp <= NOT tmp;
count <= 1;
end if;
end if;
clk12 <= tmp;

end process;

--------------------------------------------------------------------------



--generate the timing for the bus clock (scl_clk) and the data clock (data_clk)

PROCESS(clk12, reset2)
VARIABLE count : INTEGER RANGE 0 TO 160; --divider*4; --timing for clock generation
BEGIN
IF(reset2 = '0') THEN --reset asserted
stretch <= '0';
count := 0;
ELSIF(clk12'EVENT AND clk12 = '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(clk12, reset2)
BEGIN
IF(reset2 = '0') THEN --reset asserted
--return to initial state
busy <= '1'; --indicate not available
scl_ena <= '0'; --sets scl high impedance
sda_int <= '1'; --sets sda high impedance
bit_cnt <= 7; --restarts data bit counter
data_rd <= "11111111"; --clear data read port
state <= ready;
ELSIF(clk12'EVENT AND clk12 = '1') THEN
IF(data_clk = '1' AND data_clk_prev = '0') THEN --data clock rising edge

CASE state IS

WHEN ready =>
busy <= '0';

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';
state <=martha;
ELSE
state <= stop; -- means enable = 0 goto stop
END IF;

WHEN martha =>

IF (resume = '1') THEN
addr_rw <= addr & rw; --collect requested slave address and command
data_tx <= data_wr; --collect requested data to write
busy <= '1';
state <= yellow;
ELSE
state <= martha;
END IF;


WHEN yellow =>
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
state <= start;
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
END IF;
WHEN rd => --receiving slave data
data_rx(bit_cnt) <= sda; --receive current slave data bit

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;

-------------------------------------------------------------------------------

Test Bench

 

 

--------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 14:20:14 07/28/2020
-- Design Name:
-- Module Name: C:/Users/MIDI/Desktop/XILINX_PROJECTS/I2C_TEST/Martha.vhd
-- Project Name: I2C_TEST
-- Target Device:
-- Tool versions:
-- Description:
--
-- VHDL Test Bench Created by ISE for module: i2c_master
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
-- Notes:
-- This testbench has been automatically generated using types std_logic and
-- std_logic_vector for the ports of the unit under test. Xilinx recommends
-- that these types always be used for the top-level I/O of a design in order
-- to guarantee that the testbench will bind correctly to the post-implementation
-- simulation model.
--------------------------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--USE ieee.numeric_std.ALL;

ENTITY Martha IS
END Martha;

ARCHITECTURE behavior OF Martha IS

-- Component Declaration for the Unit Under Test (UUT)

COMPONENT i2c_master
PORT(
clkMN : IN std_logic;
reset2 : IN std_logic;
ena : IN std_logic;
addr : IN std_logic_vector(6 downto 0);
rw : IN std_logic;
resume : IN std_logic;
data_wr : IN std_logic_vector(7 downto 0);
busy : OUT std_logic;
data_rd : OUT std_logic_vector(7 downto 0);
sda : INOUT std_logic;
scl : INOUT std_logic
);
END COMPONENT;

--Inputs
signal resume : std_logic := '0';
signal clkMN : std_logic := '0';
signal reset2 : std_logic := '0';
signal ena : std_logic := '0';
signal addr : std_logic_vector(6 downto 0) := (others => '0');
signal rw : std_logic := '0';
signal data_wr : std_logic_vector(7 downto 0) := (others => '0');

--BiDirs
signal sda : std_logic;
signal scl : std_logic;

--Outputs
signal busy : std_logic;
signal data_rd : std_logic_vector(7 downto 0);

-- Clock period definitions
constant clkMN_period : time := 20 ns;

BEGIN

-- Instantiate the Unit Under Test (UUT)
uut: i2c_master PORT MAP (
clkMN => clkMN,
reset2 => reset2,
ena => ena,
resume => resume,
addr => addr,
rw => rw,
data_wr => data_wr,
busy => busy,
data_rd => data_rd,
sda => sda,
scl => scl
);

-- hold reset state for 100 ns.
-- Clock process definitions
clkMN_process :process
begin
clkMN <= '0';
wait for clkMN_period/2;
clkMN <= '1';
wait for clkMN_period/2;
end process;

-- Stimulus process
stim_proc: process
begin
-- hold reset state for 100 ns.
wait for 100 ns;
reset2 <='0';
wait for 100ns;
reset2 <='1';
wait for 100 ns;


scl <= 'H';
sda <= 'H';
wait for 100 ns;
resume <= '0';
rw <= '0';
addr <= "1110000";
data_wr <= "00100001";
wait for 50 ns;
ena <= '1';
wait for 200 ns;
data_wr <= "10000001";
wait for 50 ns;
resume <= '1';
wait for 200 ns;
resume <= '0';
wait for 200 ns;
resume <= '1';
data_wr <= "11101111";
wait for 50 ns;
resume <= '0';
wait for 200 ns;
ena <= '0';
wait for 100 ns;
wait;
end process;

END;

 

0 Kudos