blob: 63e3ac7e95d0921d2677f194035aadcad951eb4d [file] [log] [blame] [edit]
/*
Copyright (c) 2017 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* I2C slave
*/
module i2c_slave #(
parameter FILTER_LEN = 4
)
(
input wire clk,
input wire rst,
/*
* Host interface
*/
input wire release_bus,
input wire [7:0] data_in,
input wire data_in_valid,
output wire data_in_ready,
input wire data_in_last,
output wire [7:0] data_out,
output wire data_out_valid,
input wire data_out_ready,
output wire data_out_last,
/*
* I2C interface
*/
input wire scl_i,
output wire scl_o,
output wire scl_t,
input wire sda_i,
output wire sda_o,
output wire sda_t,
/*
* Status
*/
output wire busy,
output wire [6:0] bus_address,
output wire bus_addressed,
output wire bus_active,
/*
* Configuration
*/
input wire enable,
input wire [6:0] device_address,
input wire [6:0] device_address_mask
);
/*
I2C
Read
__ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __
sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_\_R___A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A____/
____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____
scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP
Write
__ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __
sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_/ W \_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_/ N \__/
____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____
scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP
Operation:
This module translates I2C read and write operations into AXI stream transfers.
Bytes written over I2C will be delayed by one byte time so that the last byte
in a write operation can be accurately marked. When reading, the module will
stretch SCL by holding it low until a data byte is presented at the AXI stream
input.
Control:
release_bus
releases control over bus
Status:
busy
module is communicating over the bus
bus_address
active address on bus when module is addressed
bus_addressed
module is currently addressed on the bus
bus_active
bus is active, not necessarily controlled by this module
Parameters:
device_address
address of slave device
device_address_mask
select which bits of device address to compare, set to 7'h7f
to check all bits (single address device)
Example of interfacing with tristate pins:
(this will work for any tristate bus)
assign scl_i = scl_pin;
assign scl_pin = scl_t ? 1'bz : scl_o;
assign sda_i = sda_pin;
assign sda_pin = sda_t ? 1'bz : sda_o;
Equivalent code that does not use *_t connections:
(we can get away with this because I2C is open-drain)
assign scl_i = scl_pin;
assign scl_pin = scl_o ? 1'bz : 1'b0;
assign sda_i = sda_pin;
assign sda_pin = sda_o ? 1'bz : 1'b0;
Example of two interconnected I2C devices:
assign scl_1_i = scl_1_o & scl_2_o;
assign scl_2_i = scl_1_o & scl_2_o;
assign sda_1_i = sda_1_o & sda_2_o;
assign sda_2_i = sda_1_o & sda_2_o;
Example of two I2C devices sharing the same pins:
assign scl_1_i = scl_pin;
assign scl_2_i = scl_pin;
assign scl_pin = (scl_1_o & scl_2_o) ? 1'bz : 1'b0;
assign sda_1_i = sda_pin;
assign sda_2_i = sda_pin;
assign sda_pin = (sda_1_o & sda_2_o) ? 1'bz : 1'b0;
Notes:
scl_o should not be connected directly to scl_i, only via AND logic or a tristate
I/O pin. This would prevent devices from stretching the clock period.
*/
localparam [4:0]
STATE_IDLE = 4'd0,
STATE_ADDRESS = 4'd1,
STATE_ACK = 4'd2,
STATE_WRITE_1 = 4'd3,
STATE_WRITE_2 = 4'd4,
STATE_READ_1 = 4'd5,
STATE_READ_2 = 4'd6,
STATE_READ_3 = 4'd7;
reg [4:0] state_reg = STATE_IDLE, state_next;
reg [6:0] addr_reg = 7'd0, addr_next;
reg [7:0] data_reg = 8'd0, data_next;
reg data_valid_reg = 1'b0, data_valid_next;
reg data_out_reg_valid_reg = 1'b0, data_out_reg_valid_next;
reg last_reg = 1'b0, last_next;
reg mode_read_reg = 1'b0, mode_read_next;
reg [3:0] bit_count_reg = 4'd0, bit_count_next;
reg data_in_ready_reg = 1'b0, data_in_ready_next;
reg [7:0] data_out_reg = 8'd0, data_out_next;
reg data_out_valid_reg = 1'b0, data_out_valid_next;
reg data_out_last_reg = 1'b0, data_out_last_next;
reg [FILTER_LEN-1:0] scl_i_filter = {FILTER_LEN{1'b1}};
reg [FILTER_LEN-1:0] sda_i_filter = {FILTER_LEN{1'b1}};
reg scl_i_reg = 1'b1;
reg sda_i_reg = 1'b1;
reg scl_o_reg = 1'b1, scl_o_next;
reg sda_o_reg = 1'b1, sda_o_next;
reg last_scl_i_reg = 1'b1;
reg last_sda_i_reg = 1'b1;
reg busy_reg = 1'b0;
reg bus_active_reg = 1'b0;
reg bus_addressed_reg = 1'b0, bus_addressed_next;
assign bus_address = addr_reg;
assign data_in_ready = data_in_ready_reg;
assign data_out = data_out_reg;
assign data_out_valid = data_out_valid_reg;
assign data_out_last = data_out_last_reg;
assign scl_o = scl_o_reg;
assign scl_t = scl_o_reg;
assign sda_o = sda_o_reg;
assign sda_t = sda_o_reg;
assign busy = busy_reg;
assign bus_active = bus_active_reg;
assign bus_addressed = bus_addressed_reg;
assign scl_posedge = scl_i_reg && !last_scl_i_reg;
assign scl_negedge = !scl_i_reg && last_scl_i_reg;
assign sda_posedge = sda_i_reg && !last_sda_i_reg;
assign sda_negedge = !sda_i_reg && last_sda_i_reg;
assign start_bit = sda_negedge && scl_i_reg;
assign stop_bit = sda_posedge && scl_i_reg;
always @* begin
state_next = STATE_IDLE;
addr_next = addr_reg;
data_next = data_reg;
data_valid_next = data_valid_reg;
data_out_reg_valid_next = data_out_reg_valid_reg;
last_next = last_reg;
mode_read_next = mode_read_reg;
bit_count_next = bit_count_reg;
data_in_ready_next = 1'b0;
data_out_next = data_out_reg;
data_out_valid_next = data_out_valid_reg && !data_out_ready;
data_out_last_next = data_out_last_reg;
scl_o_next = scl_o_reg;
sda_o_next = sda_o_reg;
bus_addressed_next = bus_addressed_reg;
if (start_bit) begin
// got start bit, latch out data, read address
data_valid_next = 1'b0;
data_out_reg_valid_next = 1'b0;
bit_count_next = 4'd7;
data_out_last_next = 1'b1;
data_out_valid_next = data_out_reg_valid_reg;
bus_addressed_next = 1'b0;
state_next = STATE_ADDRESS;
end else if (release_bus || stop_bit) begin
// got stop bit or release bus command, latch out data, return to idle
data_valid_next = 1'b0;
data_out_reg_valid_next = 1'b0;
data_out_last_next = 1'b1;
data_out_valid_next = data_out_reg_valid_reg;
bus_addressed_next = 1'b0;
state_next = STATE_IDLE;
end else begin
case (state_reg)
STATE_IDLE: begin
// line idle
data_valid_next = 1'b0;
data_out_reg_valid_next = 1'b0;
bus_addressed_next = 1'b0;
state_next = STATE_IDLE;
end
STATE_ADDRESS: begin
// read address
if (scl_posedge) begin
if (bit_count_reg > 0) begin
// shift in address
bit_count_next = bit_count_reg-1;
data_next = {data_reg[6:0], sda_i_reg};
state_next = STATE_ADDRESS;
end else begin
// check address
if (enable && (device_address & device_address_mask) == (data_reg[6:0] & device_address_mask)) begin
// it's a match, save read/write bit and send ACK
addr_next = data_reg[6:0];
mode_read_next = sda_i_reg;
bus_addressed_next = 1'b1;
state_next = STATE_ACK;
end else begin
// no match, return to idle
state_next = STATE_IDLE;
end
end
end else begin
state_next = STATE_ADDRESS;
end
end
STATE_ACK: begin
// send ACK bit
if (scl_negedge) begin
sda_o_next = 1'b0;
bit_count_next = 4'd7;
if (mode_read_reg) begin
// reading
data_in_ready_next = 1'b1;
data_valid_next = 1'b0;
state_next = STATE_READ_1;
end else begin
// writing
state_next = STATE_WRITE_1;
end
end else begin
state_next = STATE_ACK;
end
end
STATE_WRITE_1: begin
// write data byte
if (scl_negedge || !scl_o_reg) begin
sda_o_next = 1'b1;
if (data_out_valid && !data_out_ready) begin
// data waiting in output register, so stretch clock
scl_o_next = 1'b0;
state_next = STATE_WRITE_1;
end else begin
scl_o_next = 1'b1;
if (data_valid_reg) begin
// store data in output register
data_out_next = data_reg;
data_out_last_next = 1'b0;
end
data_valid_next = 1'b0;
data_out_reg_valid_next = data_valid_reg;
state_next = STATE_WRITE_2;
end
end else begin
state_next = STATE_WRITE_1;
end
end
STATE_WRITE_2: begin
// write data byte
if (scl_posedge) begin
// shift in data bit
data_next = {data_reg[6:0], sda_i_reg};
if (bit_count_reg > 0) begin
bit_count_next = bit_count_reg-1;
state_next = STATE_WRITE_2;
end else begin
// latch out previous data byte since we now know it's not the last one
data_out_valid_next = data_out_reg_valid_reg;
data_out_reg_valid_next = 1'b0;
data_valid_next = 1'b1;
state_next = STATE_ACK;
end
end else begin
state_next = STATE_WRITE_2;
end
end
STATE_READ_1: begin
// read data byte
if (data_in_ready && data_in_valid) begin
// data valid; latch it in
data_in_ready_next = 1'b0;
data_next = data_in;
data_valid_next = 1'b1;
end else begin
// keep ready high if we're waiting for data
data_in_ready_next = !data_valid_reg;
end
if (scl_negedge || !scl_o_reg) begin
// shift out data bit
if (!data_valid_reg) begin
// waiting for data, so stretch clock
scl_o_next = 1'b0;
state_next = STATE_READ_1;
end else begin
scl_o_next = 1'b1;
{sda_o_next, data_next} = {data_reg, 1'b0};
if (bit_count_reg > 0) begin
bit_count_next = bit_count_reg-1;
state_next = STATE_READ_1;
end else begin
state_next = STATE_READ_2;
end
end
end else begin
state_next = STATE_READ_1;
end
end
STATE_READ_2: begin
// read ACK bit
if (scl_negedge) begin
// release SDA
sda_o_next = 1'b1;
state_next = STATE_READ_3;
end else begin
state_next = STATE_READ_2;
end
end
STATE_READ_3: begin
// read ACK bit
if (scl_posedge) begin
if (sda_i_reg) begin
// NACK, return to idle
state_next = STATE_IDLE;
end else begin
// ACK, read another byte
bit_count_next = 4'd7;
data_in_ready_next = 1'b1;
data_valid_next = 1'b0;
state_next = STATE_READ_1;
end
end else begin
state_next = STATE_READ_3;
end
end
endcase
end
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
data_in_ready_reg <= 1'b0;
data_out_valid_reg <= 1'b0;
scl_o_reg <= 1'b1;
sda_o_reg <= 1'b1;
busy_reg <= 1'b0;
bus_active_reg <= 1'b0;
bus_addressed_reg <= 1'b0;
end else begin
state_reg <= state_next;
data_in_ready_reg <= data_in_ready_next;
data_out_valid_reg <= data_out_valid_next;
scl_o_reg <= scl_o_next;
sda_o_reg <= sda_o_next;
busy_reg <= !(state_reg == STATE_IDLE);
if (start_bit) begin
bus_active_reg <= 1'b1;
end else if (stop_bit) begin
bus_active_reg <= 1'b0;
end else begin
bus_active_reg <= bus_active_reg;
end
bus_addressed_reg <= bus_addressed_next;
end
addr_reg <= addr_next;
data_reg <= data_next;
data_valid_reg <= data_valid_next;
data_out_reg_valid_reg <= data_out_reg_valid_next;
last_reg <= last_next;
mode_read_reg <= mode_read_next;
bit_count_reg <= bit_count_next;
data_out_reg <= data_out_next;
data_out_last_reg <= data_out_last_next;
scl_i_filter <= (scl_i_filter << 1) | scl_i;
sda_i_filter <= (sda_i_filter << 1) | sda_i;
if (scl_i_filter == {FILTER_LEN{1'b1}}) begin
scl_i_reg <= 1'b1;
end else if (scl_i_filter == {FILTER_LEN{1'b0}}) begin
scl_i_reg <= 1'b0;
end
if (sda_i_filter == {FILTER_LEN{1'b1}}) begin
sda_i_reg <= 1'b1;
end else if (sda_i_filter == {FILTER_LEN{1'b0}}) begin
sda_i_reg <= 1'b0;
end
last_scl_i_reg <= scl_i_reg;
last_sda_i_reg <= sda_i_reg;
end
endmodule