OpenCores
URL https://opencores.org/ocsvn/i2crepeater/i2crepeater/trunk

Subversion Repositories i2crepeater

[/] [i2crepeater/] [trunk/] [i2crepeater.v] - Rev 2

Compare with Previous | Blame | View Log

////////////////////////////////////////////////////////////////////////////////
/// \file i2crepeater.v
/// \brief I2C Repeater module
///
/// \author Eric Gallimore <egallimore@whoi.edu>
///
///
/// Incorporates a start/stop detection technique by gszakacs found on Xilinx forums.
///
////////////////////////////////////////////////////////////////////////////////
 
module i2crepeater(reset, system_clk, master_scl, master_sda, slave_scl, slave_sda, sda_direction_tap, start_stop_tap, incycle_tap);
 
	input reset;
	input system_clk;
	inout master_sda;
	input master_scl;
	output slave_scl;
	inout slave_sda;
	output sda_direction_tap;	// For probing
	output start_stop_tap;		// For probing
	output incycle_tap;			// For probing
 
 
	// Direction of SDA signal
	enum { MOSI, MISO } sda_direction;
	assign sda_direction_tap = (sda_direction == MISO) ? 1 : 0;
 
	// States
	enum { IDLE, ADDRESS, RWBIT, SLAVEACK, MASTERACK, DATATOSLAVE, DATAFROMSLAVE } State;
 
	// Just pass the clock through from master to slave.
	assign slave_scl = master_scl;
 
 
	// Assignment of I/O.
	assign slave_sda = (sda_direction == MOSI) ? master_sda : 'z;
	assign master_sda = (sda_direction == MISO) ? slave_sda : 'z;
 
 
	// Sample the SDA and SCL lines to do start and stop detection
	// Only the master can generate start and stop signals.
	logic [4:0] scl_samples; // Multiple samples used to debounce signal.
	logic [4:0] sda_samples;
	logic scl_new;
	logic scl_old;
	logic sda_new;
	logic sda_old;
	logic got_start;
	logic got_stop;
	always @(posedge system_clk or posedge reset) begin
		if (reset) begin
			scl_samples <= 5'b11111;  // I2C signals are pulled up by default
			sda_samples <= 5'b11111;
			scl_new <= 1;
			scl_old <= 1;
			sda_new <= 1;
			sda_old <= 1;
			got_start <= 0;
			got_stop <= 0;
 
		end else begin
			// Sample the signals and store them
			// Shift the old samples left by one bit to make room for the new ones.
			scl_samples <= {scl_samples[3:0], master_scl};
			sda_samples <= {sda_samples[3:0], master_sda};
			// Keep track of previous values
			scl_old <= scl_new;
			sda_old <= sda_new;
			// Turn samples into scl and sda values.
			if (scl_samples == 5'b11111)
				scl_new <= 1;
			else if (scl_samples == 5'b00000)
				scl_new <= 0;
 
			if (sda_samples == 5'b11111)
				sda_new <= 1;
			else if (sda_samples == 5'b00000)
				sda_new <= 0;
 
			// Do edge detection to find start and stop values.
			// If SCL remained high while SDA fell, we have a a start.
			if (scl_new & scl_old & !sda_new & sda_old)
				got_start <= 1;
			else if (!scl_new & !scl_old)	// clear got_start when SCL falls.
				got_start <= 0;
 
			// If SCL remained high while SDA rose, we have a stop.
			got_stop <= scl_new & scl_old & sda_new & !sda_old;	// will clear on next clock edge
		end
	end
 
 
 
	// Sample the data bits on the positive edge of each clock cycle
	// Get both and decide what to do with them later.
	logic master_sda_bit;
	logic slave_sda_bit;
	always @(posedge master_scl) begin
		master_sda_bit <= master_sda;
		slave_sda_bit <= slave_sda;
	end
 
 
 
	// Bit counter with state tracking
	logic [3:0] bitcount;	// Counts 8 bits of data plus the ACK
	logic isread;	// Is this packet a read request?
	logic newcycle;
 
	always @(negedge master_scl or posedge reset or posedge got_start or posedge got_stop) begin
		if (reset || got_start || got_stop) begin
			State <= IDLE;
			sda_direction <= MOSI;
			bitcount <= 4'h7;
			isread <= 0;
		end else begin
			case (State)
				IDLE: begin
						// We are not idle any more, and are now waiting for an address
						State <= ADDRESS;
						bitcount <= 4'h6; // We miss an edge at the start.
					end
 
				ADDRESS: begin
						// We need to keep track of what bit we are on
						if (bitcount == 4'h1) // We have finished the 7 bit address, so the next bit is R/W
							State <= RWBIT;
						else
							bitcount <= bitcount - 4'h1;
					end
 
				RWBIT: begin
						isread <= master_sda_bit;
						sda_direction <= MISO;
						State <= SLAVEACK;
					end
 
				SLAVEACK: begin
						bitcount <= 4'h7;	// We will be waiting for a byte of data in one direction or the other
						if (isread) begin
							sda_direction <= MISO;
							State <= DATAFROMSLAVE;
						end else begin
							sda_direction <= MOSI;
							State <= DATATOSLAVE;
						end
					end
 
				DATAFROMSLAVE: begin
						// We need to keep track of what bit we're on, again.
						if (bitcount == 4'h0) begin // We just finished this byte.
							sda_direction <= MOSI;
							State <= MASTERACK;
						end else
							bitcount <= bitcount - 4'h1;
					end
 
				MASTERACK: begin
							// At this point, we will either get a start/stop or start on the next byte
							// Start/stop conditions dump us back to the beginning
							if (master_sda_bit == 1) begin	// NACK
								sda_direction <=MOSI;	// We will send a STOP next.
								State <= IDLE;
							end else begin
								bitcount <= 4'h7;	// We will be waiting for a byte of data
								sda_direction <= MISO;
								State <= DATAFROMSLAVE;
							end
						end
 
				DATATOSLAVE: begin
						// We need to keep track of what bit we're on, again.
						if (bitcount == 4'h0) begin // We just finished this byte.
							sda_direction <= MISO;
							State <= SLAVEACK;
						end else
							bitcount <= bitcount - 4'h1;
					end
			endcase
		end
	end
 
 
endmodule

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.