UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Visitor zshin
Visitor
3,977 Views
Registered: ‎08-23-2016

I2C address translation code in Verilog

 Hello all,

I have a question about I2C address translation code in Verilog.

I already asked about this topic few weeks ago, and that time I could not get any feasible solution.

Now, I designed simple address translator and have a question about malfunctioning.

 

Basically, In my system, I have one I2C Master device and 18 Slave devices so that I need to translate my target address.

For example, when my master device try to access 0x7X device then, my translator will translate to 0x3X and least of data will just sending over through my translator.

here is my translator code.

 

`timescale 1ns/1ps

module I2C_TRANSLATOR (clk, reset, en, SCL, SDA);

input clk;
input reset;
input en;

inout SCL;
inout SDA;

reg float_sda;
reg sda_out;
wire sda_in;
wire scl_in;

reg tsi, tsi_ee, cps, cps_ee;

 

// I/O buffer for the I2C open-drain signals -------
assign scl_in = SCL; 
assign sda_in = SDA;/
assign SDA = (float_sda) ? 1'bz : sda_out; 
//--------------------------------------------------

reg [7:0] in_address;
reg [7:0] out_address;


// start stop detection states ---------------------
`define NULL_DET 2'b00 
`define START_DET 2'b01 
`define STOP_DET 2'b10 
//--------------------------------------------------

reg [1:0] startStopDetState;
reg startEdgeDet;

 

// State machine parameters -------------------------
reg [3:0] state; 
reg [3:0] nxt_state; 
reg [7:0] address_count; 
reg [7:0] address_trans; 
localparam start_detect = 0; 
localparam address_decode = 1; 
localparam address_translate = 2; 
localparam address_transfer = 3; 
localparam wait_stop = 4; 
//--------------------------------------------------

 

// Debouncing and deglitch input signal ------------
reg sda, scl; 
reg [3:0] sda_sr, scl_sr; 
always @ (posedge clk or posedge reset or en) begin
if((reset == 1'b1) && (en == 1'b0))begin 
    sda_sr <= 4'hf; 
    sda <= 1; 
    scl_sr <= 4'hf; 
    scl <= 1; 
end 
else if ((reset == 1'b0) && (en == 1'b1)) begin 
    sda_sr <= {sda_sr[2:0], sda_in}; 
if (sda_sr == 4'h0) sda <= 0; 
else if (sda_sr == 4'hf) sda <= 1; 
scl_sr <= {scl_sr[2:0], scl_in}; 
if (scl_sr == 4'h0) scl <= 0; 
else if (scl_sr == 4'hf) scl <= 1; 
end 
end //
//--------------------------------------------------

always @ (state or in_address or out_address or nxt_state or float_sda or sda or scl) begin
case(state)

start_detect: begin
  if((startStopDetState == `START_DET) && (en == 1'b1))begin
  nxt_state <= address_decode; 
  address_count = 8'd7;
  address_trans = 8'd7; 
  $display ("Start_detected");
end
else begin
  nxt_state <= start_detect; 
  $display ("waiting start_detection");
end
end

address_decode: begin
  $display ("Address_decoding");
  in_address[address_count] <= sda;
  if(address_count == 0)
  nxt_state <= address_translate;
else
  address_count <= address_count -1;
end

address_translate: begin
  $display ("Address_translate");
if(in_address[7:4] == 4'h7) begin //Tsi
  $display("===> TSI");
  out_address[7:4] <= 4'h3;
  out_address[3:0] <= in_address[3:0];
  tsi <= 1'b1;
  tsi_ee <= 1'b0;
  cps <= 1'b0;
  cps_ee <= 1'b1;
  nxt_state <= address_transfer;
end
else if(in_address[7:4] == 4'h1) begin //Tsi eeprom
  $display("===> TSI_EEPROM");
  out_address[7:4] <= 4'h5;
  out_address[3:0] <= in_address[3:0];
  tsi <= 1'b0;
  tsi_ee <= 1'b1;
  cps <= 1'b0;
  cps_ee <= 1'b1;
  nxt_state <= address_transfer;
end
else if(in_address[7:4] == 4'h3) begin //CPS
  $display("===> CPS");
  out_address[7:4] <= 4'h5;
  out_address[3:0] <= in_address[3:0];
  tsi <= 1'b0;
  tsi_ee <= 1'b0;  
  cps <= 1'b1;
  cps_ee <= 1'b0; 
  nxt_state <= address_transfer;
end
else if(in_address[7:4] == 4'h0) begin //CPS eeprom
  $display("===> CPS_EEPROM");
  out_address[7:4] <= 4'h6;
  out_address[3:0] <= in_address[3:0];
  tsi <= 1'b0;
  tsi_ee <= 1'b0;
  cps <= 1'b0;
  cps_ee <= 1'b1;
  nxt_state <= address_transfer;
end
else begin
  $display("===> DO_NOT_TRANSLATE");
  out_address <= in_address;
  nxt_state <= address_transfer;
end
end
address_transfer: begin
  $display("Address_transfer");
  float_sda <= 1'b0;
  sda_out <= out_address[address_trans];
  if(address_trans == 0)begin
  nxt_state <= wait_stop;
  float_sda <= 1'b1; 
end
else
  address_trans <= address_trans-1;
end
wait_stop: begin
  float_sda <= 1'b1;
  $display("Wait for stop signal");
if((startStopDetState == `STOP_DET) && (en == 1'b1))
  nxt_state <= start_detect;
else begin
  nxt_state <= wait_stop;
  float_sda = sda;
end
end
endcase//End of case
end//End of always

// State Update ----------------------------------------
always @ (posedge clk)begin //
if (reset == 1'b1) begin //
// Reset //
float_sda <= 1'b1; //
sda_out <= 1'b0; //
state <= start_detect; //
address_count <= 8'd0; //
address_trans <= 8'd0; //
in_address <= 8'h00; //
out_address <= 8'h00; //
tsi <= 1'b0; //
tsi_ee <= 1'b0; //
cps <= 1'b0; //
cps_ee <= 1'b0; //
end //
else if ((reset == 1'b0) && (en == 1'b0)) begin //
// Reset Done but Logic OFF //
float_sda <= 1'b1; //
sda_out <= 1'b0; //
state <= start_detect; //
address_count <= 8'd0; //
address_trans <= 8'd0; //
in_address <= 8'h00; //
out_address <= 8'h00; //
tsi <= 1'b0; //
tsi_ee <= 1'b0; //
cps <= 1'b0; //
cps_ee <= 1'b0; //
end //
else if ((reset == 1'b0) && (en == 1'b1)) begin //
state <= nxt_state; //
end //
end //
//-------------------------------------------------------

// Start_Stop_detection ---------------------------
always @(posedge clk) begin
if (reset == 1'b1) begin
startStopDetState <= `NULL_DET;
startEdgeDet <= 1'b0;
end
else begin
if (scl == 1'b1 && sda_sr[2] == 1'b0 && sda_sr[3] == 1'b1)
startEdgeDet <= 1'b1;
else
startEdgeDet <= 1'b0;
if (scl == 1'b1) begin
if (sda_sr[2] == 1'b1 && sda_sr[3] == 1'b0)
startStopDetState <= `STOP_DET;
else if (sda_sr[2] == 1'b0 && sda_sr[3] == 1'b1)
startStopDetState <= `START_DET;
end
end
end
//-------------------------------------------------

endmodule

 

And I find simple I2C master code from XILINX forum. below is that one.

`timescale 1ns / 1ps
// I2C Simple Master for typical 7-bit EEPROM-like slaves.
// Not multi-master capable.

`default_nettype none

module I2C_master #(parameter freq = 100)(
inout wire SDA, // I2C Serial data line, pulled high at board level
inout wire SCL, // I2C Serial clock line, pulled high at board level
input wire clk, // System clock, wr_ctrl should be synchronous to this
input wire reset, // power-on reset - puts I2C bus into idle state
input wire [31:0] ctrl_data, // Data bus for writing the control register
input wire wr_ctrl, // Write enable for control register, also starts I2C cycles
output reg [31:0] status // Status of I2C including most recently read data
);

reg float_sda; // This is essentially SDA when we are sourcing it (open drain at pin)
reg float_scl; // This is essentially SCL when we are sourcing it (open drain at pin)
wire sda_in; // Feedback from the IOB pin for SDA
wire scl_in; // Feedback from the IOB pin for SCL

// I/O buffers for the I2C open-drain signals
// Note that even if no slaves drive SCL, you need to use feedback
// to sense the rising edge of SCL to be sure to meet hold time.
assign scl_in = SCL;
assign SCL = float_scl ? 1'bZ : 1'b0;

assign sda_in = SDA;
assign SDA = float_sda ? 1'bZ : 1'b0;

localparam t_hd_sta = 4 * freq, // Hold time on START condition, 4.0us from spec
t_low = 5 * freq, // SCL low time 4.7us from spec
t_high = 5 * freq, // SCL high time 4.0us from spec, but cycle time must be 10us
t_su_sta = 5 * freq, // SDA, SCL high before asserting start, 4.7us from spec
t_su_dat = (freq >> 2) + 1, // Data valid to SCL rising, 250ns from spec
t_hold = (freq >> 1) + 1, // SCL falling to SDA changing 0 from spec, 0.5us for AD9888
t_su_sto = 4 * freq; // SCL high to SDA high for STOP condition, 4us from spec

localparam time_width = clogb2(t_low + 1); // Declare enough bits to hold maximum delay (5 us)

reg [time_width-1:0] timer;

// Ceiling of log base 2 from the Verilog Language Reference Manual:
function integer clogb2;
input [31:0] value;
begin
value = value - 1;
for (clogb2 = 0; value > 0; clogb2 = clogb2 + 1)
value = value >> 1;
end
endfunction

reg [31:0] ctrl_reg; // I2C control register
// Bit definitions:
// 31 Read / not write.
// 30 Repeated Start. On read cycles setting this bit uses repeated start instad of stop start sequence.
// 29:24 Reserved.
// 23:17 7-bit I2C address of slave.
// 16 Don't care. Allows use of read or write address when writing this byte.
// 15:8 Register Subaddress
// 7:0 Data to write. Don't care on read cycles.

//reg [31:0] status; // I2C status register
// Bit definitions
// 31 Busy. Not ready to accept new control register writes.
// 30 Address NACK. Last address cycle resulted in no acknowledge from slave.
// 29 Data NACK. Last data write cycle resulted in no acknowledge from slave.
// 28 Read. Most recently completed cycle was a read. Data in bits 7:0 is valid.
// 27 Overrun. An attempt was made to write the ctrl_reg while busy. Cleared
// on successful write of ctrl_reg.
// 26 Initializing - waiting for SDA to go high after module reset
// 25:8 Reserved. Tied to zero.
// 7:0 Read data. Valid after a read cycle when bit 28 is set.

reg [26:0] shift_reg; // Data to shift out. Includes ack positions: 1 = NACK or slave ack.

reg [4:0] bit_count; // Counts bits during shift states.

reg [3:0] state; // Main state machine state variable
reg [3:0] rtn_state; // Return state for "subroutines"

reg sda, scl; // debounced, deglitched SDA and SCL inputs

reg wr_cyc; // Every access starts with a write cycle for subaddress.

reg [7:0] read_data; // Input shift register for reads

reg [3:0] scl_startup_count; // Clock SCL at least 12 times after SDA is detected high

// Debounce and deglitch input signals
reg [3:0] sda_sr, scl_sr;
always @ (posedge clk or posedge reset)
if (reset)
begin
sda_sr <= 4'b1111; // Start up assuming quiescent state of inputs
sda <= 1;
scl_sr <= 4'b1111; // Start up assuming quiescent state of inputs
scl <= 1;
end
else
begin
sda_sr <= {sda_sr[2:0], sda_in};
if (sda_sr == 4'b0000) sda <= 0;
else if (sda_sr == 4'b1111) sda <= 1;
scl_sr <= {scl_sr[2:0], scl_in};
if (scl_sr == 4'b0000) scl <= 0;
else if (scl_sr == 4'b1111) scl <= 1;
end

// Define states:
localparam pre_start_up = 0,
start_up = 1,
idle = 2,
start = 3,
clock_low = 4,
shift_data = 5,
clock_high = 6,
stop = 7,
spin = 15;

always @ (posedge clk or posedge reset)
if (reset)
begin
timer <= t_low;
state <= pre_start_up;
rtn_state <= pre_start_up;
ctrl_reg <= 0;
status <= 32'h84000000; // Busy, initializing
shift_reg <= {27{1'b1}};
bit_count <= 0;
float_sda <= 1;
float_scl <= 1;
wr_cyc <= 1;
read_data <= 0;
scl_startup_count <= 0;
end
else
begin
if (wr_ctrl)
begin
if (status[31]) // busy
begin
status[27] <= 1; // Set overrun flag on unsuccessful attempt to write ctrl_reg
end
else // not busy
begin
ctrl_reg <= ctrl_data;
status[27] <= 0; // Clear overrun flag on successful write to ctrl_reg
end
end
case (state)
// In pre-start-up state wait for SDA to go high while clocking SCL as necessary
pre_start_up:
begin
if (timer == 0) // every 5 us
begin
if (float_scl)
begin
if (sda && (scl_startup_count == 12)) // quiescent?
begin
scl_startup_count <= 0;
state <= start_up;
end
else
begin
float_scl <= 0; // Start another SCL clock cycle if SDA is still low
timer <= t_low;
scl_startup_count <= scl_startup_count + 1;
end
end
else // Currently driving SCL
begin
float_scl <= 1; // Release SCL
timer <= t_low;
end
end
else if (scl | !float_scl) // Start timing after rising edge of SCL if not driven
begin
timer <= timer - 1;
end
end
// In start-up state, generate a start and stop with no clocks in between
start_up:
begin
if (timer == 0) // every 5 us
begin
timer <= t_low;
scl_startup_count <= scl_startup_count + 1;
if (scl_startup_count == 2) float_sda <= 0; // Create a start condition
if (scl_startup_count == 12) float_sda <= 1; // Create a stop condition
if (scl_startup_count == 15) state <= idle;
end
else
begin
timer <= timer - 1;
end
end
idle:
begin
float_sda <= 1;
float_scl <= 1;
wr_cyc <= 1;
status[31] <= 0; // Not busy
status[26] <= 0; // Done initialization
if (wr_ctrl & !status[31]) // successful write to ctrl_reg
begin
state <= start;
status[31] <= 1; // go busy
end
end
start:
begin
// Create high to low transition on SDA.
// Both SDA and SCL were high at least 4.7us before entering this state
float_sda <= 0;
float_scl <= 1;
if (!sda) // Continue when we see sda driven low
begin
// 7-bit ADDR, R/WN, Slave Ack, 8-bit Subaddr, Slave Ack, 8-bit Data, Slave Ack
// Data byte and final Slave Ack do not apply for reads
// For Stop then start, SDA must be low after last ack cycle.
// For repeated start, SDA must be high after last ack cycle.
if (ctrl_reg[31]) // reading requires subaddr write then data read
if (wr_cyc)
shift_reg <= {ctrl_reg[23:17],1'b0,1'b1,ctrl_reg[15:8],1'b1,ctrl_reg[30],7'b0,1'b0};
else
shift_reg <= {ctrl_reg[23:17],1'b1,1'b1,8'hff,1'b1,8'b0,1'b0};
else // Writing
shift_reg <= {ctrl_reg[23:17],1'b0,1'b1,ctrl_reg[15:8],1'b1,ctrl_reg[7:0],1'b1};
bit_count <= 0;
timer <= t_hd_sta; // 4.0us from the spec
rtn_state <= clock_low;
state <= spin;
end
end
clock_low:
begin
// Assert SCL low and when it is low, wait for t_hold before changing SDA
float_scl <= 0;
if (!scl) // Continue when SCL line has gone low
begin
timer <= t_hold; // extra 0.5 us for AD9888
rtn_state <= shift_data;
state <= spin;
end
end
shift_data:
begin
// Shift data onto the SDA line
float_sda <= shift_reg[26];
shift_reg <= {shift_reg[25:0],1'b0}; // shift left
timer <= t_low; // 4.7us min from spec
rtn_state <= clock_high;
state <= spin;
end
clock_high:
begin
// Release low drive on SCL and when it goes high
// sample SDA and move on
float_scl <= 1;
if (scl)
begin
bit_count <= bit_count + 1;
if (bit_count == 8) // Address ACK cycle
begin
status[30] <= sda; // SDA should be driven low for slave ACK
end
else if ((bit_count == 17) & wr_cyc || (bit_count == 26)) // Data ACK cycles
begin
status[29] <= sda; // SDA should be driven low for slave ACK
end
if ((bit_count == 18) & ctrl_reg[31] // Reading and past first data ack
|| (bit_count == 27)) // Past second data ack
begin
timer <= t_su_sto; // 4.0us from spec
rtn_state <= stop;
state <= spin;
end
else
begin
if (bit_count != 17) read_data <= {read_data[6:0],sda}; // shift data in, MSB first
timer <= t_high; // 4.0us from spec, but use 5.0 instead to meet cycle time
rtn_state <= clock_low;
state <= spin;
end
end
end
stop:
begin
// We get here twice for read cycles, once for writes. On reads if
// using repeated start we don't need to wait as long before asserting SDA
// for start since t_su_sto has already elapsed (4.0us)
float_sda <= 1; // SDA will already be high in the case of repeated start
if (sda)
begin
if (ctrl_reg[31]) // reading
begin
if (wr_cyc) // just finished sending subaddress
begin
if (ctrl_reg[30]) // repeated start
timer <= t_su_sta - t_su_sto;
else
timer <= t_su_sta;
rtn_state <= start;
end
else
begin
status[7:0] <= read_data;
status[28] <= 1;
timer <= t_su_sta; // Setup to start is same as bus-free, 4.7us from spec
rtn_state <= idle; // For writes we're all done
end
wr_cyc <= 0;
state <= spin;
end
else // writing
begin
status[28] <= 0;
timer <= t_su_sta; // Setup to start is same as bus-free, 4.7us from spec
rtn_state <= idle; // For writes we're all done
state <= spin;
end
end
end
spin:
begin
// stay in this state for requested time period then "return"
if (timer > 0)
begin
timer <= timer - 1;
end
else
begin
state <= rtn_state;
end
end
endcase
end

endmodule

`default_nettype wire

Now, when I run this code, my translator could detect Start and it can decode the address as well as translation too.

but somehow it can not transfer to translated address through SDA line.

please help me out

Thanks

Ziho Shin (Joe)

Tags (2)
0 Kudos
2 Replies
Moderator
Moderator
3,902 Views
Registered: ‎07-31-2012

Re: I2C address translation code in Verilog

Hi,

 

Have you referred to AXI IIC IP rtl code?

 

Regards

Praveen


-------------------------------------------------------------------------
Don’t forget to reply, kudo, and accept as solution.
-------------------------------------------------------------------------
0 Kudos
Visitor zshin
Visitor
3,886 Views
Registered: ‎08-23-2016

Re: I2C address translation code in Verilog

Parven,

Thank you for your comments.

actually I did not seeing AXII IIC IP yet, but the thing is I need to design IIC address translator. not a IIC Master device.

please help me :)

Joe

0 Kudos