URL
https://opencores.org/ocsvn/i2c/i2c/trunk
Subversion Repositories i2c
Compare Revisions
- This comparison shows the changes necessary to convert path
/i2c/tags/rel_1/rtl
- from Rev 44 to Rev 68
- ↔ Reverse comparison
Rev 44 → Rev 68
/verilog/i2c_master_top.v
0,0 → 1,293
///////////////////////////////////////////////////////////////////// |
//// //// |
//// WISHBONE revB.2 compliant I2C Master controller Top-level //// |
//// //// |
//// //// |
//// Author: Richard Herveille //// |
//// richard@asics.ws //// |
//// www.asics.ws //// |
//// //// |
//// Downloaded from: http://www.opencores.org/projects/i2c/ //// |
//// //// |
///////////////////////////////////////////////////////////////////// |
//// //// |
//// Copyright (C) 2001 Richard Herveille //// |
//// richard@asics.ws //// |
//// //// |
//// This source file may be used and distributed without //// |
//// restriction provided that this copyright statement is not //// |
//// removed from the file and that any derivative work contains //// |
//// the original copyright notice and the associated disclaimer.//// |
//// //// |
//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// |
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// |
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// |
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// |
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// |
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// |
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// |
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// |
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// |
//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// |
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// |
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// |
//// POSSIBILITY OF SUCH DAMAGE. //// |
//// //// |
///////////////////////////////////////////////////////////////////// |
|
// CVS Log |
// |
// $Id: i2c_master_top.v,v 1.10 2003-09-01 10:34:38 rherveille Exp $ |
// |
// $Date: 2003-09-01 10:34:38 $ |
// $Revision: 1.10 $ |
// $Author: rherveille $ |
// $Locker: $ |
// $State: Exp $ |
// |
// Change History: |
// $Log: not supported by cvs2svn $ |
// Revision 1.9 2003/01/09 16:44:45 rherveille |
// Fixed a bug in the Command Register declaration. |
// |
// Revision 1.8 2002/12/26 16:05:12 rherveille |
// Small code simplifications |
// |
// Revision 1.7 2002/12/26 15:02:32 rherveille |
// Core is now a Multimaster I2C controller |
// |
// Revision 1.6 2002/11/30 22:24:40 rherveille |
// Cleaned up code |
// |
// Revision 1.5 2001/11/10 10:52:55 rherveille |
// Changed PRER reset value from 0x0000 to 0xffff, conform specs. |
// |
|
// synopsys translate_off |
`include "timescale.v" |
// synopsys translate_on |
|
`include "i2c_master_defines.v" |
|
module i2c_master_top( |
wb_clk_i, wb_rst_i, arst_i, wb_adr_i, wb_dat_i, wb_dat_o, |
wb_we_i, wb_stb_i, wb_cyc_i, wb_ack_o, wb_inta_o, |
scl_pad_i, scl_pad_o, scl_padoen_o, sda_pad_i, sda_pad_o, sda_padoen_o ); |
|
// parameters |
parameter ARST_LVL = 1'b0; // asynchronous reset level |
|
// |
// inputs & outputs |
// |
|
// wishbone signals |
input wb_clk_i; // master clock input |
input wb_rst_i; // synchronous active high reset |
input arst_i; // asynchronous reset |
input [2:0] wb_adr_i; // lower address bits |
input [7:0] wb_dat_i; // databus input |
output [7:0] wb_dat_o; // databus output |
input wb_we_i; // write enable input |
input wb_stb_i; // stobe/core select signal |
input wb_cyc_i; // valid bus cycle input |
output wb_ack_o; // bus cycle acknowledge output |
output wb_inta_o; // interrupt request signal output |
|
reg [7:0] wb_dat_o; |
reg wb_ack_o; |
reg wb_inta_o; |
|
// I2C signals |
// i2c clock line |
input scl_pad_i; // SCL-line input |
output scl_pad_o; // SCL-line output (always 1'b0) |
output scl_padoen_o; // SCL-line output enable (active low) |
|
// i2c data line |
input sda_pad_i; // SDA-line input |
output sda_pad_o; // SDA-line output (always 1'b0) |
output sda_padoen_o; // SDA-line output enable (active low) |
|
|
// |
// variable declarations |
// |
|
// registers |
reg [15:0] prer; // clock prescale register |
reg [ 7:0] ctr; // control register |
reg [ 7:0] txr; // transmit register |
wire [ 7:0] rxr; // receive register |
reg [ 7:0] cr; // command register |
wire [ 7:0] sr; // status register |
|
// done signal: command completed, clear command register |
wire done; |
|
// core enable signal |
wire core_en; |
wire ien; |
|
// status register signals |
wire irxack; |
reg rxack; // received aknowledge from slave |
reg tip; // transfer in progress |
reg irq_flag; // interrupt pending flag |
wire i2c_busy; // bus busy (start signal detected) |
wire i2c_al; // i2c bus arbitration lost |
reg al; // status register arbitration lost bit |
|
// |
// module body |
// |
|
// generate internal reset |
wire rst_i = arst_i ^ ARST_LVL; |
|
// generate wishbone signals |
wire wb_wacc = wb_cyc_i & wb_stb_i & wb_we_i; |
|
// generate acknowledge output signal |
always @(posedge wb_clk_i) |
wb_ack_o <= #1 wb_cyc_i & wb_stb_i & ~wb_ack_o; // because timing is always honored |
|
// assign DAT_O |
always @(posedge wb_clk_i) |
begin |
case (wb_adr_i) // synopsis full_case parallel_case |
3'b000: wb_dat_o <= #1 prer[ 7:0]; |
3'b001: wb_dat_o <= #1 prer[15:8]; |
3'b010: wb_dat_o <= #1 ctr; |
3'b011: wb_dat_o <= #1 rxr; // write is transmit register (txr) |
3'b100: wb_dat_o <= #1 sr; // write is command register (cr) |
3'b101: wb_dat_o <= #1 txr; |
3'b110: wb_dat_o <= #1 cr; |
3'b111: wb_dat_o <= #1 0; // reserved |
endcase |
end |
|
// generate registers |
always @(posedge wb_clk_i or negedge rst_i) |
if (!rst_i) |
begin |
prer <= #1 16'hffff; |
ctr <= #1 8'h0; |
txr <= #1 8'h0; |
end |
else if (wb_rst_i) |
begin |
prer <= #1 16'hffff; |
ctr <= #1 8'h0; |
txr <= #1 8'h0; |
end |
else |
if (wb_wacc) |
case (wb_adr_i) // synopsis full_case parallel_case |
3'b000 : prer [ 7:0] <= #1 wb_dat_i; |
3'b001 : prer [15:8] <= #1 wb_dat_i; |
3'b010 : ctr <= #1 wb_dat_i; |
3'b011 : txr <= #1 wb_dat_i; |
endcase |
|
// generate command register (special case) |
always @(posedge wb_clk_i or negedge rst_i) |
if (~rst_i) |
cr <= #1 8'h0; |
else if (wb_rst_i) |
cr <= #1 8'h0; |
else if (wb_wacc) |
begin |
if (core_en & (wb_adr_i == 3'b100) ) |
cr <= #1 wb_dat_i; |
end |
else |
begin |
if (done | i2c_al) |
cr[7:4] <= #1 4'h0; // clear command bits when done |
// or when aribitration lost |
cr[2:1] <= #1 2'b0; // reserved bits |
cr[0] <= #1 2'b0; // clear IRQ_ACK bit |
end |
|
|
// decode command register |
wire sta = cr[7]; |
wire sto = cr[6]; |
wire rd = cr[5]; |
wire wr = cr[4]; |
wire ack = cr[3]; |
wire iack = cr[0]; |
|
// decode control register |
assign core_en = ctr[7]; |
assign ien = ctr[6]; |
|
// hookup byte controller block |
i2c_master_byte_ctrl byte_controller ( |
.clk ( wb_clk_i ), |
.rst ( wb_rst_i ), |
.nReset ( rst_i ), |
.ena ( core_en ), |
.clk_cnt ( prer ), |
.start ( sta ), |
.stop ( sto ), |
.read ( rd ), |
.write ( wr ), |
.ack_in ( ack ), |
.din ( txr ), |
.cmd_ack ( done ), |
.ack_out ( irxack ), |
.dout ( rxr ), |
.i2c_busy ( i2c_busy ), |
.i2c_al ( i2c_al ), |
.scl_i ( scl_pad_i ), |
.scl_o ( scl_pad_o ), |
.scl_oen ( scl_padoen_o ), |
.sda_i ( sda_pad_i ), |
.sda_o ( sda_pad_o ), |
.sda_oen ( sda_padoen_o ) |
); |
|
// status register block + interrupt request signal |
always @(posedge wb_clk_i or negedge rst_i) |
if (!rst_i) |
begin |
al <= #1 1'b0; |
rxack <= #1 1'b0; |
tip <= #1 1'b0; |
irq_flag <= #1 1'b0; |
end |
else if (wb_rst_i) |
begin |
al <= #1 1'b0; |
rxack <= #1 1'b0; |
tip <= #1 1'b0; |
irq_flag <= #1 1'b0; |
end |
else |
begin |
al <= #1 i2c_al | (al & ~sta); |
rxack <= #1 irxack; |
tip <= #1 (rd | wr); |
irq_flag <= #1 (done | i2c_al | irq_flag) & ~iack; // interrupt request flag is always generated |
end |
|
// generate interrupt request signals |
always @(posedge wb_clk_i or negedge rst_i) |
if (!rst_i) |
wb_inta_o <= #1 1'b0; |
else if (wb_rst_i) |
wb_inta_o <= #1 1'b0; |
else |
wb_inta_o <= #1 irq_flag && ien; // interrupt signal is only generated when IEN (interrupt enable bit is set) |
|
// assign status register bits |
assign sr[7] = rxack; |
assign sr[6] = i2c_busy; |
assign sr[5] = al; |
assign sr[4:2] = 3'h0; // reserved |
assign sr[1] = tip; |
assign sr[0] = irq_flag; |
|
endmodule |
/verilog/i2c_master_byte_ctrl.v
0,0 → 1,343
///////////////////////////////////////////////////////////////////// |
//// //// |
//// WISHBONE rev.B2 compliant I2C Master byte-controller //// |
//// //// |
//// //// |
//// Author: Richard Herveille //// |
//// richard@asics.ws //// |
//// www.asics.ws //// |
//// //// |
//// Downloaded from: http://www.opencores.org/projects/i2c/ //// |
//// //// |
///////////////////////////////////////////////////////////////////// |
//// //// |
//// Copyright (C) 2001 Richard Herveille //// |
//// richard@asics.ws //// |
//// //// |
//// This source file may be used and distributed without //// |
//// restriction provided that this copyright statement is not //// |
//// removed from the file and that any derivative work contains //// |
//// the original copyright notice and the associated disclaimer.//// |
//// //// |
//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// |
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// |
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// |
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// |
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// |
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// |
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// |
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// |
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// |
//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// |
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// |
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// |
//// POSSIBILITY OF SUCH DAMAGE. //// |
//// //// |
///////////////////////////////////////////////////////////////////// |
|
// CVS Log |
// |
// $Id: i2c_master_byte_ctrl.v,v 1.6 2003-08-09 07:01:33 rherveille Exp $ |
// |
// $Date: 2003-08-09 07:01:33 $ |
// $Revision: 1.6 $ |
// $Author: rherveille $ |
// $Locker: $ |
// $State: Exp $ |
// |
// Change History: |
// $Log: not supported by cvs2svn $ |
// Revision 1.5 2002/12/26 15:02:32 rherveille |
// Core is now a Multimaster I2C controller |
// |
// Revision 1.4 2002/11/30 22:24:40 rherveille |
// Cleaned up code |
// |
// Revision 1.3 2001/11/05 11:59:25 rherveille |
// Fixed wb_ack_o generation bug. |
// Fixed bug in the byte_controller statemachine. |
// Added headers. |
// |
|
// synopsys translate_off |
`include "timescale.v" |
// synopsys translate_on |
|
`include "i2c_master_defines.v" |
|
module i2c_master_byte_ctrl ( |
clk, rst, nReset, ena, clk_cnt, start, stop, read, write, ack_in, din, |
cmd_ack, ack_out, dout, i2c_busy, i2c_al, scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen ); |
|
// |
// inputs & outputs |
// |
input clk; // master clock |
input rst; // synchronous active high reset |
input nReset; // asynchronous active low reset |
input ena; // core enable signal |
|
input [15:0] clk_cnt; // 4x SCL |
|
// control inputs |
input start; |
input stop; |
input read; |
input write; |
input ack_in; |
input [7:0] din; |
|
// status outputs |
output cmd_ack; |
reg cmd_ack; |
output ack_out; |
reg ack_out; |
output i2c_busy; |
output i2c_al; |
output [7:0] dout; |
|
// I2C signals |
input scl_i; |
output scl_o; |
output scl_oen; |
input sda_i; |
output sda_o; |
output sda_oen; |
|
|
// |
// Variable declarations |
// |
|
// statemachine |
parameter [4:0] ST_IDLE = 5'b0_0000; |
parameter [4:0] ST_START = 5'b0_0001; |
parameter [4:0] ST_READ = 5'b0_0010; |
parameter [4:0] ST_WRITE = 5'b0_0100; |
parameter [4:0] ST_ACK = 5'b0_1000; |
parameter [4:0] ST_STOP = 5'b1_0000; |
|
// signals for bit_controller |
reg [3:0] core_cmd; |
reg core_txd; |
wire core_ack, core_rxd; |
|
// signals for shift register |
reg [7:0] sr; //8bit shift register |
reg shift, ld; |
|
// signals for state machine |
wire go; |
reg [2:0] dcnt; |
wire cnt_done; |
|
// |
// Module body |
// |
|
// hookup bit_controller |
i2c_master_bit_ctrl bit_controller ( |
.clk ( clk ), |
.rst ( rst ), |
.nReset ( nReset ), |
.ena ( ena ), |
.clk_cnt ( clk_cnt ), |
.cmd ( core_cmd ), |
.cmd_ack ( core_ack ), |
.busy ( i2c_busy ), |
.al ( i2c_al ), |
.din ( core_txd ), |
.dout ( core_rxd ), |
.scl_i ( scl_i ), |
.scl_o ( scl_o ), |
.scl_oen ( scl_oen ), |
.sda_i ( sda_i ), |
.sda_o ( sda_o ), |
.sda_oen ( sda_oen ) |
); |
|
// generate go-signal |
assign go = (read | write | stop) & ~cmd_ack; |
|
// assign dout output to shift-register |
assign dout = sr; |
|
// generate shift register |
always @(posedge clk or negedge nReset) |
if (!nReset) |
sr <= #1 8'h0; |
else if (rst) |
sr <= #1 8'h0; |
else if (ld) |
sr <= #1 din; |
else if (shift) |
sr <= #1 {sr[6:0], core_rxd}; |
|
// generate counter |
always @(posedge clk or negedge nReset) |
if (!nReset) |
dcnt <= #1 3'h0; |
else if (rst) |
dcnt <= #1 3'h0; |
else if (ld) |
dcnt <= #1 3'h7; |
else if (shift) |
dcnt <= #1 dcnt - 3'h1; |
|
assign cnt_done = ~(|dcnt); |
|
// |
// state machine |
// |
reg [4:0] c_state; // synopsis enum_state |
|
always @(posedge clk or negedge nReset) |
if (!nReset) |
begin |
core_cmd <= #1 `I2C_CMD_NOP; |
core_txd <= #1 1'b0; |
shift <= #1 1'b0; |
ld <= #1 1'b0; |
cmd_ack <= #1 1'b0; |
c_state <= #1 ST_IDLE; |
ack_out <= #1 1'b0; |
end |
else if (rst | i2c_al) |
begin |
core_cmd <= #1 `I2C_CMD_NOP; |
core_txd <= #1 1'b0; |
shift <= #1 1'b0; |
ld <= #1 1'b0; |
cmd_ack <= #1 1'b0; |
c_state <= #1 ST_IDLE; |
ack_out <= #1 1'b0; |
end |
else |
begin |
// initially reset all signals |
core_txd <= #1 sr[7]; |
shift <= #1 1'b0; |
ld <= #1 1'b0; |
cmd_ack <= #1 1'b0; |
|
case (c_state) // synopsis full_case parallel_case |
ST_IDLE: |
if (go) |
begin |
if (start) |
begin |
c_state <= #1 ST_START; |
core_cmd <= #1 `I2C_CMD_START; |
end |
else if (read) |
begin |
c_state <= #1 ST_READ; |
core_cmd <= #1 `I2C_CMD_READ; |
end |
else if (write) |
begin |
c_state <= #1 ST_WRITE; |
core_cmd <= #1 `I2C_CMD_WRITE; |
end |
else // stop |
begin |
c_state <= #1 ST_STOP; |
core_cmd <= #1 `I2C_CMD_STOP; |
|
// generate command acknowledge signal |
cmd_ack <= #1 1'b1; |
end |
|
ld <= #1 1'b1; |
end |
|
ST_START: |
if (core_ack) |
begin |
if (read) |
begin |
c_state <= #1 ST_READ; |
core_cmd <= #1 `I2C_CMD_READ; |
end |
else |
begin |
c_state <= #1 ST_WRITE; |
core_cmd <= #1 `I2C_CMD_WRITE; |
end |
|
ld <= #1 1'b1; |
end |
|
ST_WRITE: |
if (core_ack) |
if (cnt_done) |
begin |
c_state <= #1 ST_ACK; |
core_cmd <= #1 `I2C_CMD_READ; |
end |
else |
begin |
c_state <= #1 ST_WRITE; // stay in same state |
core_cmd <= #1 `I2C_CMD_WRITE; // write next bit |
shift <= #1 1'b1; |
end |
|
ST_READ: |
if (core_ack) |
begin |
if (cnt_done) |
begin |
c_state <= #1 ST_ACK; |
core_cmd <= #1 `I2C_CMD_WRITE; |
end |
else |
begin |
c_state <= #1 ST_READ; // stay in same state |
core_cmd <= #1 `I2C_CMD_READ; // read next bit |
end |
|
shift <= #1 1'b1; |
core_txd <= #1 ack_in; |
end |
|
ST_ACK: |
if (core_ack) |
begin |
if (stop) |
begin |
c_state <= #1 ST_STOP; |
core_cmd <= #1 `I2C_CMD_STOP; |
end |
else |
begin |
c_state <= #1 ST_IDLE; |
core_cmd <= #1 `I2C_CMD_NOP; |
|
// generate command acknowledge signal |
cmd_ack <= #1 1'b1; |
end |
|
// assign ack_out output to bit_controller_rxd (contains last received bit) |
ack_out <= #1 core_rxd; |
|
// // generate command acknowledge signal |
// cmd_ack <= #1 1'b1; |
|
core_txd <= #1 1'b1; |
end |
else |
core_txd <= #1 ack_in; |
|
ST_STOP: |
if (core_ack) |
begin |
c_state <= #1 ST_IDLE; |
core_cmd <= #1 `I2C_CMD_NOP; |
|
// generate command acknowledge signal |
cmd_ack <= #1 1'b1; |
end |
|
endcase |
end |
endmodule |
/verilog/i2c_master_bit_ctrl.v
0,0 → 1,527
///////////////////////////////////////////////////////////////////// |
//// //// |
//// WISHBONE rev.B2 compliant I2C Master bit-controller //// |
//// //// |
//// //// |
//// Author: Richard Herveille //// |
//// richard@asics.ws //// |
//// www.asics.ws //// |
//// //// |
//// Downloaded from: http://www.opencores.org/projects/i2c/ //// |
//// //// |
///////////////////////////////////////////////////////////////////// |
//// //// |
//// Copyright (C) 2001 Richard Herveille //// |
//// richard@asics.ws //// |
//// //// |
//// This source file may be used and distributed without //// |
//// restriction provided that this copyright statement is not //// |
//// removed from the file and that any derivative work contains //// |
//// the original copyright notice and the associated disclaimer.//// |
//// //// |
//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// |
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// |
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// |
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// |
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// |
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// |
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// |
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// |
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// |
//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// |
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// |
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// |
//// POSSIBILITY OF SUCH DAMAGE. //// |
//// //// |
///////////////////////////////////////////////////////////////////// |
|
// CVS Log |
// |
// $Id: i2c_master_bit_ctrl.v,v 1.10 2003-08-09 07:01:33 rherveille Exp $ |
// |
// $Date: 2003-08-09 07:01:33 $ |
// $Revision: 1.10 $ |
// $Author: rherveille $ |
// $Locker: $ |
// $State: Exp $ |
// |
// Change History: |
// $Log: not supported by cvs2svn $ |
// Revision 1.9 2003/03/10 14:26:37 rherveille |
// Fixed cmd_ack generation item (no bug). |
// |
// Revision 1.8 2003/02/05 00:06:10 rherveille |
// Fixed a bug where the core would trigger an erroneous 'arbitration lost' interrupt after being reset, when the reset pulse width < 3 clk cycles. |
// |
// Revision 1.7 2002/12/26 16:05:12 rherveille |
// Small code simplifications |
// |
// Revision 1.6 2002/12/26 15:02:32 rherveille |
// Core is now a Multimaster I2C controller |
// |
// Revision 1.5 2002/11/30 22:24:40 rherveille |
// Cleaned up code |
// |
// Revision 1.4 2002/10/30 18:10:07 rherveille |
// Fixed some reported minor start/stop generation timing issuess. |
// |
// Revision 1.3 2002/06/15 07:37:03 rherveille |
// Fixed a small timing bug in the bit controller.\nAdded verilog simulation environment. |
// |
// Revision 1.2 2001/11/05 11:59:25 rherveille |
// Fixed wb_ack_o generation bug. |
// Fixed bug in the byte_controller statemachine. |
// Added headers. |
// |
|
// |
///////////////////////////////////// |
// Bit controller section |
///////////////////////////////////// |
// |
// Translate simple commands into SCL/SDA transitions |
// Each command has 5 states, A/B/C/D/idle |
// |
// start: SCL ~~~~~~~~~~\____ |
// SDA ~~~~~~~~\______ |
// x | A | B | C | D | i |
// |
// repstart SCL ____/~~~~\___ |
// SDA __/~~~\______ |
// x | A | B | C | D | i |
// |
// stop SCL ____/~~~~~~~~ |
// SDA ==\____/~~~~~ |
// x | A | B | C | D | i |
// |
//- write SCL ____/~~~~\____ |
// SDA ==X=========X= |
// x | A | B | C | D | i |
// |
//- read SCL ____/~~~~\____ |
// SDA XXXX=====XXXX |
// x | A | B | C | D | i |
// |
|
// Timing: Normal mode Fast mode |
/////////////////////////////////////////////////////////////////////// |
// Fscl 100KHz 400KHz |
// Th_scl 4.0us 0.6us High period of SCL |
// Tl_scl 4.7us 1.3us Low period of SCL |
// Tsu:sta 4.7us 0.6us setup time for a repeated start condition |
// Tsu:sto 4.0us 0.6us setup time for a stop conditon |
// Tbuf 4.7us 1.3us Bus free time between a stop and start condition |
// |
|
// synopsys translate_off |
`include "timescale.v" |
// synopsys translate_on |
|
`include "i2c_master_defines.v" |
|
module i2c_master_bit_ctrl( |
clk, rst, nReset, |
clk_cnt, ena, cmd, cmd_ack, busy, al, din, dout, |
scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen |
); |
|
// |
// inputs & outputs |
// |
input clk; |
input rst; |
input nReset; |
input ena; // core enable signal |
|
input [15:0] clk_cnt; // clock prescale value |
|
input [3:0] cmd; |
output cmd_ack; // command complete acknowledge |
reg cmd_ack; |
output busy; // i2c bus busy |
reg busy; |
output al; // i2c bus arbitration lost |
reg al; |
|
input din; |
output dout; |
reg dout; |
|
// I2C lines |
input scl_i; // i2c clock line input |
output scl_o; // i2c clock line output |
output scl_oen; // i2c clock line output enable (active low) |
reg scl_oen; |
input sda_i; // i2c data line input |
output sda_o; // i2c data line output |
output sda_oen; // i2c data line output enable (active low) |
reg sda_oen; |
|
|
// |
// variable declarations |
// |
|
reg sSCL, sSDA; // synchronized SCL and SDA inputs |
reg dscl_oen; // delayed scl_oen |
reg sda_chk; // check SDA output (Multi-master arbitration) |
reg clk_en; // clock generation signals |
wire slave_wait; |
// reg [15:0] cnt = clk_cnt; // clock divider counter (simulation) |
reg [15:0] cnt; // clock divider counter (synthesis) |
|
// |
// module body |
// |
|
// whenever the slave is not ready it can delay the cycle by pulling SCL low |
// delay scl_oen |
always @(posedge clk) |
dscl_oen <= #1 scl_oen; |
|
assign slave_wait = dscl_oen && !sSCL; |
|
|
// generate clk enable signal |
always @(posedge clk or negedge nReset) |
if(~nReset) |
begin |
cnt <= #1 16'h0; |
clk_en <= #1 1'b1; |
end |
else if (rst) |
begin |
cnt <= #1 16'h0; |
clk_en <= #1 1'b1; |
end |
else if ( ~|cnt || ~ena) |
if (~slave_wait) |
begin |
cnt <= #1 clk_cnt; |
clk_en <= #1 1'b1; |
end |
else |
begin |
cnt <= #1 cnt; |
clk_en <= #1 1'b0; |
end |
else |
begin |
cnt <= #1 cnt - 16'h1; |
clk_en <= #1 1'b0; |
end |
|
|
// generate bus status controller |
reg dSCL, dSDA; |
reg sta_condition; |
reg sto_condition; |
|
// synchronize SCL and SDA inputs |
// reduce metastability risc |
always @(posedge clk or negedge nReset) |
if (~nReset) |
begin |
sSCL <= #1 1'b1; |
sSDA <= #1 1'b1; |
|
dSCL <= #1 1'b1; |
dSDA <= #1 1'b1; |
end |
else if (rst) |
begin |
sSCL <= #1 1'b1; |
sSDA <= #1 1'b1; |
|
dSCL <= #1 1'b1; |
dSDA <= #1 1'b1; |
end |
else |
begin |
sSCL <= #1 scl_i; |
sSDA <= #1 sda_i; |
|
dSCL <= #1 sSCL; |
dSDA <= #1 sSDA; |
end |
|
// detect start condition => detect falling edge on SDA while SCL is high |
// detect stop condition => detect rising edge on SDA while SCL is high |
always @(posedge clk or negedge nReset) |
if (~nReset) |
begin |
sta_condition <= #1 1'b0; |
sto_condition <= #1 1'b0; |
end |
else if (rst) |
begin |
sta_condition <= #1 1'b0; |
sto_condition <= #1 1'b0; |
end |
else |
begin |
sta_condition <= #1 ~sSDA & dSDA & sSCL; |
sto_condition <= #1 sSDA & ~dSDA & sSCL; |
end |
|
// generate i2c bus busy signal |
always @(posedge clk or negedge nReset) |
if(!nReset) |
busy <= #1 1'b0; |
else if (rst) |
busy <= #1 1'b0; |
else |
busy <= #1 (sta_condition | busy) & ~sto_condition; |
|
// generate arbitration lost signal |
// aribitration lost when: |
// 1) master drives SDA high, but the i2c bus is low |
// 2) stop detected while not requested |
reg cmd_stop; |
always @(posedge clk or negedge nReset) |
if (~nReset) |
cmd_stop <= #1 1'b0; |
else if (rst) |
cmd_stop <= #1 1'b0; |
else if (clk_en) |
cmd_stop <= #1 cmd == `I2C_CMD_STOP; |
|
always @(posedge clk or negedge nReset) |
if (~nReset) |
al <= #1 1'b0; |
else if (rst) |
al <= #1 1'b0; |
else |
al <= #1 (sda_chk & ~sSDA & sda_oen) | (sto_condition & ~cmd_stop); |
|
|
// generate dout signal (store SDA on rising edge of SCL) |
always @(posedge clk) |
if(sSCL & ~dSCL) |
dout <= #1 sSDA; |
|
// generate statemachine |
|
// nxt_state decoder |
parameter [16:0] idle = 17'b0_0000_0000_0000_0000; |
parameter [16:0] start_a = 17'b0_0000_0000_0000_0001; |
parameter [16:0] start_b = 17'b0_0000_0000_0000_0010; |
parameter [16:0] start_c = 17'b0_0000_0000_0000_0100; |
parameter [16:0] start_d = 17'b0_0000_0000_0000_1000; |
parameter [16:0] start_e = 17'b0_0000_0000_0001_0000; |
parameter [16:0] stop_a = 17'b0_0000_0000_0010_0000; |
parameter [16:0] stop_b = 17'b0_0000_0000_0100_0000; |
parameter [16:0] stop_c = 17'b0_0000_0000_1000_0000; |
parameter [16:0] stop_d = 17'b0_0000_0001_0000_0000; |
parameter [16:0] rd_a = 17'b0_0000_0010_0000_0000; |
parameter [16:0] rd_b = 17'b0_0000_0100_0000_0000; |
parameter [16:0] rd_c = 17'b0_0000_1000_0000_0000; |
parameter [16:0] rd_d = 17'b0_0001_0000_0000_0000; |
parameter [16:0] wr_a = 17'b0_0010_0000_0000_0000; |
parameter [16:0] wr_b = 17'b0_0100_0000_0000_0000; |
parameter [16:0] wr_c = 17'b0_1000_0000_0000_0000; |
parameter [16:0] wr_d = 17'b1_0000_0000_0000_0000; |
|
reg [16:0] c_state; // synopsis enum_state |
|
always @(posedge clk or negedge nReset) |
if (!nReset) |
begin |
c_state <= #1 idle; |
cmd_ack <= #1 1'b0; |
scl_oen <= #1 1'b1; |
sda_oen <= #1 1'b1; |
sda_chk <= #1 1'b0; |
end |
else if (rst | al) |
begin |
c_state <= #1 idle; |
cmd_ack <= #1 1'b0; |
scl_oen <= #1 1'b1; |
sda_oen <= #1 1'b1; |
sda_chk <= #1 1'b0; |
end |
else |
begin |
cmd_ack <= #1 1'b0; // default no command acknowledge + assert cmd_ack only 1clk cycle |
|
if (clk_en) |
case (c_state) // synopsis full_case parallel_case |
// idle state |
idle: |
begin |
case (cmd) // synopsis full_case parallel_case |
`I2C_CMD_START: |
c_state <= #1 start_a; |
|
`I2C_CMD_STOP: |
c_state <= #1 stop_a; |
|
`I2C_CMD_WRITE: |
c_state <= #1 wr_a; |
|
`I2C_CMD_READ: |
c_state <= #1 rd_a; |
|
default: |
c_state <= #1 idle; |
endcase |
|
scl_oen <= #1 scl_oen; // keep SCL in same state |
sda_oen <= #1 sda_oen; // keep SDA in same state |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
// start |
start_a: |
begin |
c_state <= #1 start_b; |
scl_oen <= #1 scl_oen; // keep SCL in same state |
sda_oen <= #1 1'b1; // set SDA high |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
start_b: |
begin |
c_state <= #1 start_c; |
scl_oen <= #1 1'b1; // set SCL high |
sda_oen <= #1 1'b1; // keep SDA high |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
start_c: |
begin |
c_state <= #1 start_d; |
scl_oen <= #1 1'b1; // keep SCL high |
sda_oen <= #1 1'b0; // set SDA low |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
start_d: |
begin |
c_state <= #1 start_e; |
scl_oen <= #1 1'b1; // keep SCL high |
sda_oen <= #1 1'b0; // keep SDA low |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
start_e: |
begin |
c_state <= #1 idle; |
cmd_ack <= #1 1'b1; |
scl_oen <= #1 1'b0; // set SCL low |
sda_oen <= #1 1'b0; // keep SDA low |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
// stop |
stop_a: |
begin |
c_state <= #1 stop_b; |
scl_oen <= #1 1'b0; // keep SCL low |
sda_oen <= #1 1'b0; // set SDA low |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
stop_b: |
begin |
c_state <= #1 stop_c; |
scl_oen <= #1 1'b1; // set SCL high |
sda_oen <= #1 1'b0; // keep SDA low |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
stop_c: |
begin |
c_state <= #1 stop_d; |
scl_oen <= #1 1'b1; // keep SCL high |
sda_oen <= #1 1'b0; // keep SDA low |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
stop_d: |
begin |
c_state <= #1 idle; |
cmd_ack <= #1 1'b1; |
scl_oen <= #1 1'b1; // keep SCL high |
sda_oen <= #1 1'b1; // set SDA high |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
// read |
rd_a: |
begin |
c_state <= #1 rd_b; |
scl_oen <= #1 1'b0; // keep SCL low |
sda_oen <= #1 1'b1; // tri-state SDA |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
rd_b: |
begin |
c_state <= #1 rd_c; |
scl_oen <= #1 1'b1; // set SCL high |
sda_oen <= #1 1'b1; // keep SDA tri-stated |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
rd_c: |
begin |
c_state <= #1 rd_d; |
scl_oen <= #1 1'b1; // keep SCL high |
sda_oen <= #1 1'b1; // keep SDA tri-stated |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
rd_d: |
begin |
c_state <= #1 idle; |
cmd_ack <= #1 1'b1; |
scl_oen <= #1 1'b0; // set SCL low |
sda_oen <= #1 1'b1; // keep SDA tri-stated |
sda_chk <= #1 1'b0; // don't check SDA output |
end |
|
// write |
wr_a: |
begin |
c_state <= #1 wr_b; |
scl_oen <= #1 1'b0; // keep SCL low |
sda_oen <= #1 din; // set SDA |
sda_chk <= #1 1'b0; // don't check SDA output (SCL low) |
end |
|
wr_b: |
begin |
c_state <= #1 wr_c; |
scl_oen <= #1 1'b1; // set SCL high |
sda_oen <= #1 din; // keep SDA |
sda_chk <= #1 1'b1; // check SDA output |
end |
|
wr_c: |
begin |
c_state <= #1 wr_d; |
scl_oen <= #1 1'b1; // keep SCL high |
sda_oen <= #1 din; |
sda_chk <= #1 1'b1; // check SDA output |
end |
|
wr_d: |
begin |
c_state <= #1 idle; |
cmd_ack <= #1 1'b1; |
scl_oen <= #1 1'b0; // set SCL low |
sda_oen <= #1 din; |
sda_chk <= #1 1'b0; // don't check SDA output (SCL low) |
end |
|
endcase |
end |
|
|
// assign scl and sda output (always gnd) |
assign scl_o = 1'b0; |
assign sda_o = 1'b0; |
|
endmodule |
/verilog/i2c_master_defines.v
0,0 → 1,59
///////////////////////////////////////////////////////////////////// |
//// //// |
//// WISHBONE rev.B2 compliant I2C Master controller defines //// |
//// //// |
//// //// |
//// Author: Richard Herveille //// |
//// richard@asics.ws //// |
//// www.asics.ws //// |
//// //// |
//// Downloaded from: http://www.opencores.org/projects/i2c/ //// |
//// //// |
///////////////////////////////////////////////////////////////////// |
//// //// |
//// Copyright (C) 2001 Richard Herveille //// |
//// richard@asics.ws //// |
//// //// |
//// This source file may be used and distributed without //// |
//// restriction provided that this copyright statement is not //// |
//// removed from the file and that any derivative work contains //// |
//// the original copyright notice and the associated disclaimer.//// |
//// //// |
//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// |
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// |
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// |
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// |
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// |
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// |
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// |
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// |
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// |
//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// |
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// |
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// |
//// POSSIBILITY OF SUCH DAMAGE. //// |
//// //// |
///////////////////////////////////////////////////////////////////// |
|
// CVS Log |
// |
// $Id: i2c_master_defines.v,v 1.3 2001-11-05 11:59:25 rherveille Exp $ |
// |
// $Date: 2001-11-05 11:59:25 $ |
// $Revision: 1.3 $ |
// $Author: rherveille $ |
// $Locker: $ |
// $State: Exp $ |
// |
// Change History: |
// $Log: not supported by cvs2svn $ |
|
|
// I2C registers wishbone addresses |
|
// bitcontroller states |
`define I2C_CMD_NOP 4'b0000 |
`define I2C_CMD_START 4'b0001 |
`define I2C_CMD_STOP 4'b0010 |
`define I2C_CMD_WRITE 4'b0100 |
`define I2C_CMD_READ 4'b1000 |
/verilog/timescale.v
0,0 → 1,2
`timescale 1ns / 10ps |
|
/vhdl/i2c_master_bit_ctrl.vhd
0,0 → 1,473
--------------------------------------------------------------------- |
---- ---- |
---- WISHBONE revB2 I2C Master Core; bit-controller ---- |
---- ---- |
---- ---- |
---- Author: Richard Herveille ---- |
---- richard@asics.ws ---- |
---- www.asics.ws ---- |
---- ---- |
---- Downloaded from: http://www.opencores.org/projects/i2c/ ---- |
---- ---- |
--------------------------------------------------------------------- |
---- ---- |
---- Copyright (C) 2000 Richard Herveille ---- |
---- richard@asics.ws ---- |
---- ---- |
---- This source file may be used and distributed without ---- |
---- restriction provided that this copyright statement is not ---- |
---- removed from the file and that any derivative work contains ---- |
---- the original copyright notice and the associated disclaimer.---- |
---- ---- |
---- THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY ---- |
---- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ---- |
---- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ---- |
---- FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR ---- |
---- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ---- |
---- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ---- |
---- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE ---- |
---- GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ---- |
---- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ---- |
---- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ---- |
---- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ---- |
---- OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ---- |
---- POSSIBILITY OF SUCH DAMAGE. ---- |
---- ---- |
--------------------------------------------------------------------- |
|
-- CVS Log |
-- |
-- $Id: i2c_master_bit_ctrl.vhd,v 1.9 2003-08-12 14:48:37 rherveille Exp $ |
-- |
-- $Date: 2003-08-12 14:48:37 $ |
-- $Revision: 1.9 $ |
-- $Author: rherveille $ |
-- $Locker: $ |
-- $State: Exp $ |
-- |
-- Change History: |
-- $Log: not supported by cvs2svn $ |
-- Revision 1.8 2003/08/09 07:01:13 rherveille |
-- Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line. |
-- Fixed a potential bug in the byte controller's host-acknowledge generation. |
-- |
-- Revision 1.7 2003/02/05 00:06:02 rherveille |
-- Fixed a bug where the core would trigger an erroneous 'arbitration lost' interrupt after being reset, when the reset pulse width < 3 clk cycles. |
-- |
-- Revision 1.6 2003/02/01 02:03:06 rherveille |
-- Fixed a few 'arbitration lost' bugs. VHDL version only. |
-- |
-- Revision 1.5 2002/12/26 16:05:47 rherveille |
-- Core is now a Multimaster I2C controller. |
-- |
-- Revision 1.4 2002/11/30 22:24:37 rherveille |
-- Cleaned up code |
-- |
-- Revision 1.3 2002/10/30 18:09:53 rherveille |
-- Fixed some reported minor start/stop generation timing issuess. |
-- |
-- Revision 1.2 2002/06/15 07:37:04 rherveille |
-- Fixed a small timing bug in the bit controller.\nAdded verilog simulation environment. |
-- |
-- Revision 1.1 2001/11/05 12:02:33 rherveille |
-- Split i2c_master_core.vhd into separate files for each entity; same layout as verilog version. |
-- Code updated, is now up-to-date to doc. rev.0.4. |
-- Added headers. |
-- |
|
|
-- |
------------------------------------- |
-- Bit controller section |
------------------------------------ |
-- |
-- Translate simple commands into SCL/SDA transitions |
-- Each command has 5 states, A/B/C/D/idle |
-- |
-- start: SCL ~~~~~~~~~~~~~~\____ |
-- SDA XX/~~~~~~~\______ |
-- x | A | B | C | D | i |
-- |
-- repstart SCL ______/~~~~~~~\___ |
-- SDA __/~~~~~~~\______ |
-- x | A | B | C | D | i |
-- |
-- stop SCL _______/~~~~~~~~~~~ |
-- SDA ==\___________/~~~~~ |
-- x | A | B | C | D | i |
-- |
--- write SCL ______/~~~~~~~\____ |
-- SDA XXX===============XX |
-- x | A | B | C | D | i |
-- |
--- read SCL ______/~~~~~~~\____ |
-- SDA XXXXXXX=XXXXXXXXXXX |
-- x | A | B | C | D | i |
-- |
|
-- Timing: Normal mode Fast mode |
----------------------------------------------------------------- |
-- Fscl 100KHz 400KHz |
-- Th_scl 4.0us 0.6us High period of SCL |
-- Tl_scl 4.7us 1.3us Low period of SCL |
-- Tsu:sta 4.7us 0.6us setup time for a repeated start condition |
-- Tsu:sto 4.0us 0.6us setup time for a stop conditon |
-- Tbuf 4.7us 1.3us Bus free time between a stop and start condition |
-- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.std_logic_arith.all; |
|
entity i2c_master_bit_ctrl is |
port ( |
clk : in std_logic; |
rst : in std_logic; |
nReset : in std_logic; |
ena : in std_logic; -- core enable signal |
|
clk_cnt : in unsigned(15 downto 0); -- clock prescale value |
|
cmd : in std_logic_vector(3 downto 0); |
cmd_ack : out std_logic; -- command completed |
busy : out std_logic; -- i2c bus busy |
al : out std_logic; -- arbitration lost |
|
din : in std_logic; |
dout : out std_logic; |
|
-- i2c lines |
scl_i : in std_logic; -- i2c clock line input |
scl_o : out std_logic; -- i2c clock line output |
scl_oen : out std_logic; -- i2c clock line output enable, active low |
sda_i : in std_logic; -- i2c data line input |
sda_o : out std_logic; -- i2c data line output |
sda_oen : out std_logic -- i2c data line output enable, active low |
); |
end entity i2c_master_bit_ctrl; |
|
architecture structural of i2c_master_bit_ctrl is |
constant I2C_CMD_NOP : std_logic_vector(3 downto 0) := "0000"; |
constant I2C_CMD_START : std_logic_vector(3 downto 0) := "0001"; |
constant I2C_CMD_STOP : std_logic_vector(3 downto 0) := "0010"; |
constant I2C_CMD_READ : std_logic_vector(3 downto 0) := "0100"; |
constant I2C_CMD_WRITE : std_logic_vector(3 downto 0) := "1000"; |
|
type states is (idle, start_a, start_b, start_c, start_d, start_e, |
stop_a, stop_b, stop_c, stop_d, rd_a, rd_b, rd_c, rd_d, wr_a, wr_b, wr_c, wr_d); |
signal c_state : states; |
|
signal iscl_oen, isda_oen : std_logic; -- internal I2C lines |
signal sda_chk : std_logic; -- check SDA status (multi-master arbitration) |
signal dscl_oen : std_logic; -- delayed scl_oen signals |
signal sSCL, sSDA : std_logic; -- synchronized SCL and SDA inputs |
signal clk_en, slave_wait : std_logic; -- clock generation signals |
signal ial : std_logic; -- internal arbitration lost signal |
-- signal cnt : unsigned(15 downto 0) := clk_cnt; -- clock divider counter (simulation) |
signal cnt : unsigned(15 downto 0); -- clock divider counter (synthesis) |
|
begin |
-- whenever the slave is not ready it can delay the cycle by pulling SCL low |
-- delay scl_oen |
process (clk) |
begin |
if (clk'event and clk = '1') then |
dscl_oen <= iscl_oen; |
end if; |
end process; |
slave_wait <= dscl_oen and not sSCL; |
|
-- generate clk enable signal |
gen_clken: process(clk, nReset) |
begin |
if (nReset = '0') then |
cnt <= (others => '0'); |
clk_en <= '1'; |
elsif (clk'event and clk = '1') then |
if (rst = '1') then |
cnt <= (others => '0'); |
clk_en <= '1'; |
else |
if ( (cnt = 0) or (ena = '0') ) then |
if (slave_wait = '0') then |
cnt <= clk_cnt; |
clk_en <= '1'; |
else |
cnt <= cnt; |
clk_en <= '0'; |
end if; |
else |
if (slave_wait = '0') then |
cnt <= cnt -1; |
end if; |
clk_en <= '0'; |
end if; |
end if; |
end if; |
end process gen_clken; |
|
|
-- generate bus status controller |
bus_status_ctrl: block |
signal dSCL, dSDA : std_logic; -- delayes sSCL and sSDA |
signal sta_condition : std_logic; -- start detected |
signal sto_condition : std_logic; -- stop detected |
signal cmd_stop : std_logic; -- STOP command |
signal ibusy : std_logic; -- internal busy signal |
begin |
-- synchronize SCL and SDA inputs |
synch_scl_sda: process(clk, nReset) |
begin |
if (nReset = '0') then |
sSCL <= '1'; |
sSDA <= '1'; |
|
dSCL <= '1'; |
dSDA <= '1'; |
elsif (clk'event and clk = '1') then |
if (rst = '1') then |
sSCL <= '1'; |
sSDA <= '1'; |
|
dSCL <= '1'; |
dSDA <= '1'; |
else |
sSCL <= scl_i; |
sSDA <= sda_i; |
|
dSCL <= sSCL; |
dSDA <= sSDA; |
end if; |
end if; |
end process synch_SCL_SDA; |
|
-- detect start condition => detect falling edge on SDA while SCL is high |
-- detect stop condition => detect rising edge on SDA while SCL is high |
detect_sta_sto: process(clk, nReset) |
begin |
if (nReset = '0') then |
sta_condition <= '0'; |
sto_condition <= '0'; |
elsif (clk'event and clk = '1') then |
if (rst = '1') then |
sta_condition <= '0'; |
sto_condition <= '0'; |
else |
sta_condition <= (not sSDA and dSDA) and sSCL; |
sto_condition <= (sSDA and not dSDA) and sSCL; |
end if; |
end if; |
end process detect_sta_sto; |
|
-- generate i2c-bus busy signal |
gen_busy: process(clk, nReset) |
begin |
if (nReset = '0') then |
ibusy <= '0'; |
elsif (clk'event and clk = '1') then |
if (rst = '1') then |
ibusy <= '0'; |
else |
ibusy <= (sta_condition or ibusy) and not sto_condition; |
end if; |
end if; |
end process gen_busy; |
busy <= ibusy; |
|
|
-- generate arbitration lost signal |
gen_al: process(clk, nReset) |
begin |
if (nReset = '0') then |
cmd_stop <= '0'; |
ial <= '0'; |
elsif (clk'event and clk = '1') then |
if (rst = '1') then |
cmd_stop <= '0'; |
ial <= '0'; |
else |
if (clk_en = '1' and cmd = I2C_CMD_STOP) then |
cmd_stop <= '1'; |
else |
cmd_stop <= '0'; |
end if; |
|
ial <= (sda_chk and not sSDA and isda_oen) or (sto_condition and not cmd_stop); |
end if; |
end if; |
end process gen_al; |
al <= ial; |
|
-- generate dout signal, store dout on rising edge of SCL |
gen_dout: process(clk) |
begin |
if (clk'event and clk = '1') then |
if (sSCL = '1' and dSCL = '0') then |
dout <= sSDA; |
end if; |
end if; |
end process gen_dout; |
end block bus_status_ctrl; |
|
|
-- generate statemachine |
nxt_state_decoder : process (clk, nReset, c_state, cmd) |
begin |
if (nReset = '0') then |
c_state <= idle; |
cmd_ack <= '0'; |
iscl_oen <= '1'; |
isda_oen <= '1'; |
sda_chk <= '0'; |
elsif (clk'event and clk = '1') then |
if (rst = '1' or ial = '1') then |
c_state <= idle; |
cmd_ack <= '0'; |
iscl_oen <= '1'; |
isda_oen <= '1'; |
sda_chk <= '0'; |
else |
cmd_ack <= '0'; -- default no acknowledge |
|
if (clk_en = '1') then |
case (c_state) is |
-- idle |
when idle => |
case cmd is |
when I2C_CMD_START => c_state <= start_a; |
when I2C_CMD_STOP => c_state <= stop_a; |
when I2C_CMD_WRITE => c_state <= wr_a; |
when I2C_CMD_READ => c_state <= rd_a; |
when others => c_state <= idle; -- NOP command |
end case; |
|
iscl_oen <= iscl_oen; -- keep SCL in same state |
isda_oen <= isda_oen; -- keep SDA in same state |
sda_chk <= '0'; -- don't check SDA |
|
-- start |
when start_a => |
c_state <= start_b; |
iscl_oen <= iscl_oen; -- keep SCL in same state (for repeated start) |
isda_oen <= '1'; -- set SDA high |
sda_chk <= '0'; -- don't check SDA |
|
when start_b => |
c_state <= start_c; |
iscl_oen <= '1'; -- set SCL high |
isda_oen <= '1'; -- keep SDA high |
sda_chk <= '0'; -- don't check SDA |
|
when start_c => |
c_state <= start_d; |
iscl_oen <= '1'; -- keep SCL high |
isda_oen <= '0'; -- set SDA low |
sda_chk <= '0'; -- don't check SDA |
|
when start_d => |
c_state <= start_e; |
iscl_oen <= '1'; -- keep SCL high |
isda_oen <= '0'; -- keep SDA low |
sda_chk <= '0'; -- don't check SDA |
|
when start_e => |
c_state <= idle; |
cmd_ack <= '1'; -- command completed |
iscl_oen <= '0'; -- set SCL low |
isda_oen <= '0'; -- keep SDA low |
sda_chk <= '0'; -- don't check SDA |
|
-- stop |
when stop_a => |
c_state <= stop_b; |
iscl_oen <= '0'; -- keep SCL low |
isda_oen <= '0'; -- set SDA low |
sda_chk <= '0'; -- don't check SDA |
|
when stop_b => |
c_state <= stop_c; |
iscl_oen <= '1'; -- set SCL high |
isda_oen <= '0'; -- keep SDA low |
sda_chk <= '0'; -- don't check SDA |
|
when stop_c => |
c_state <= stop_d; |
iscl_oen <= '1'; -- keep SCL high |
isda_oen <= '0'; -- keep SDA low |
sda_chk <= '0'; -- don't check SDA |
|
when stop_d => |
c_state <= idle; |
cmd_ack <= '1'; -- command completed |
iscl_oen <= '1'; -- keep SCL high |
isda_oen <= '1'; -- set SDA high |
sda_chk <= '0'; -- don't check SDA |
|
-- read |
when rd_a => |
c_state <= rd_b; |
iscl_oen <= '0'; -- keep SCL low |
isda_oen <= '1'; -- tri-state SDA |
sda_chk <= '0'; -- don't check SDA |
|
when rd_b => |
c_state <= rd_c; |
iscl_oen <= '1'; -- set SCL high |
isda_oen <= '1'; -- tri-state SDA |
sda_chk <= '0'; -- don't check SDA |
|
when rd_c => |
c_state <= rd_d; |
iscl_oen <= '1'; -- keep SCL high |
isda_oen <= '1'; -- tri-state SDA |
sda_chk <= '0'; -- don't check SDA |
|
when rd_d => |
c_state <= idle; |
cmd_ack <= '1'; -- command completed |
iscl_oen <= '0'; -- set SCL low |
isda_oen <= '1'; -- tri-state SDA |
sda_chk <= '0'; -- don't check SDA |
|
-- write |
when wr_a => |
c_state <= wr_b; |
iscl_oen <= '0'; -- keep SCL low |
isda_oen <= din; -- set SDA |
sda_chk <= '0'; -- don't check SDA (SCL low) |
|
when wr_b => |
c_state <= wr_c; |
iscl_oen <= '1'; -- set SCL high |
isda_oen <= din; -- keep SDA |
sda_chk <= '1'; -- check SDA |
|
when wr_c => |
c_state <= wr_d; |
iscl_oen <= '1'; -- keep SCL high |
isda_oen <= din; -- keep SDA |
sda_chk <= '1'; -- check SDA |
|
when wr_d => |
c_state <= idle; |
cmd_ack <= '1'; -- command completed |
iscl_oen <= '0'; -- set SCL low |
isda_oen <= din; -- keep SDA |
sda_chk <= '0'; -- don't check SDA (SCL low) |
|
when others => |
|
end case; |
end if; |
end if; |
end if; |
end process nxt_state_decoder; |
|
|
-- assign outputs |
scl_o <= '0'; |
scl_oen <= iscl_oen; |
sda_o <= '0'; |
sda_oen <= isda_oen; |
end architecture structural; |
|
/vhdl/i2c_master_byte_ctrl.vhd
0,0 → 1,364
--------------------------------------------------------------------- |
---- ---- |
---- WISHBONE revB2 compl. I2C Master Core; byte-controller ---- |
---- ---- |
---- ---- |
---- Author: Richard Herveille ---- |
---- richard@asics.ws ---- |
---- www.asics.ws ---- |
---- ---- |
---- Downloaded from: http://www.opencores.org/projects/i2c/ ---- |
---- ---- |
--------------------------------------------------------------------- |
---- ---- |
---- Copyright (C) 2000 Richard Herveille ---- |
---- richard@asics.ws ---- |
---- ---- |
---- This source file may be used and distributed without ---- |
---- restriction provided that this copyright statement is not ---- |
---- removed from the file and that any derivative work contains ---- |
---- the original copyright notice and the associated disclaimer.---- |
---- ---- |
---- THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY ---- |
---- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ---- |
---- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ---- |
---- FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR ---- |
---- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ---- |
---- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ---- |
---- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE ---- |
---- GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ---- |
---- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ---- |
---- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ---- |
---- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ---- |
---- OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ---- |
---- POSSIBILITY OF SUCH DAMAGE. ---- |
---- ---- |
--------------------------------------------------------------------- |
|
-- CVS Log |
-- |
-- $Id: i2c_master_byte_ctrl.vhd,v 1.4 2003-08-09 07:01:13 rherveille Exp $ |
-- |
-- $Date: 2003-08-09 07:01:13 $ |
-- $Revision: 1.4 $ |
-- $Author: rherveille $ |
-- $Locker: $ |
-- $State: Exp $ |
-- |
-- Change History: |
-- $Log: not supported by cvs2svn $ |
-- Revision 1.3 2002/12/26 16:05:47 rherveille |
-- Core is now a Multimaster I2C controller. |
-- |
-- Revision 1.2 2002/11/30 22:24:37 rherveille |
-- Cleaned up code |
-- |
-- Revision 1.1 2001/11/05 12:02:33 rherveille |
-- Split i2c_master_core.vhd into separate files for each entity; same layout as verilog version. |
-- Code updated, is now up-to-date to doc. rev.0.4. |
-- Added headers. |
-- |
|
|
|
|
-- |
------------------------------------------ |
-- Byte controller section |
------------------------------------------ |
-- |
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.std_logic_arith.all; |
|
entity i2c_master_byte_ctrl is |
port ( |
clk : in std_logic; |
rst : in std_logic; -- synchronous active high reset (WISHBONE compatible) |
nReset : in std_logic; -- asynchornous active low reset (FPGA compatible) |
ena : in std_logic; -- core enable signal |
|
clk_cnt : in unsigned(15 downto 0); -- 4x SCL |
|
-- input signals |
start, |
stop, |
read, |
write, |
ack_in : std_logic; |
din : in std_logic_vector(7 downto 0); |
|
-- output signals |
cmd_ack : out std_logic; -- command done |
ack_out : out std_logic; |
i2c_busy : out std_logic; -- arbitration lost |
i2c_al : out std_logic; -- i2c bus busy |
dout : out std_logic_vector(7 downto 0); |
|
-- i2c lines |
scl_i : in std_logic; -- i2c clock line input |
scl_o : out std_logic; -- i2c clock line output |
scl_oen : out std_logic; -- i2c clock line output enable, active low |
sda_i : in std_logic; -- i2c data line input |
sda_o : out std_logic; -- i2c data line output |
sda_oen : out std_logic -- i2c data line output enable, active low |
); |
end entity i2c_master_byte_ctrl; |
|
architecture structural of i2c_master_byte_ctrl is |
component i2c_master_bit_ctrl is |
port ( |
clk : in std_logic; |
rst : in std_logic; |
nReset : in std_logic; |
ena : in std_logic; -- core enable signal |
|
clk_cnt : in unsigned(15 downto 0); -- clock prescale value |
|
cmd : in std_logic_vector(3 downto 0); |
cmd_ack : out std_logic; -- command done |
busy : out std_logic; -- i2c bus busy |
al : out std_logic; -- arbitration lost |
|
din : in std_logic; |
dout : out std_logic; |
|
-- i2c lines |
scl_i : in std_logic; -- i2c clock line input |
scl_o : out std_logic; -- i2c clock line output |
scl_oen : out std_logic; -- i2c clock line output enable, active low |
sda_i : in std_logic; -- i2c data line input |
sda_o : out std_logic; -- i2c data line output |
sda_oen : out std_logic -- i2c data line output enable, active low |
); |
end component i2c_master_bit_ctrl; |
|
-- commands for bit_controller block |
constant I2C_CMD_NOP : std_logic_vector(3 downto 0) := "0000"; |
constant I2C_CMD_START : std_logic_vector(3 downto 0) := "0001"; |
constant I2C_CMD_STOP : std_logic_vector(3 downto 0) := "0010"; |
constant I2C_CMD_READ : std_logic_vector(3 downto 0) := "0100"; |
constant I2C_CMD_WRITE : std_logic_vector(3 downto 0) := "1000"; |
|
-- signals for bit_controller |
signal core_cmd : std_logic_vector(3 downto 0); |
signal core_ack, core_txd, core_rxd : std_logic; |
signal al : std_logic; |
|
-- signals for shift register |
signal sr : std_logic_vector(7 downto 0); -- 8bit shift register |
signal shift, ld : std_logic; |
|
-- signals for state machine |
signal go, host_ack : std_logic; |
signal dcnt : unsigned(2 downto 0); -- data counter |
signal cnt_done : std_logic; |
|
begin |
-- hookup bit_controller |
bit_ctrl: i2c_master_bit_ctrl port map( |
clk => clk, |
rst => rst, |
nReset => nReset, |
ena => ena, |
clk_cnt => clk_cnt, |
cmd => core_cmd, |
cmd_ack => core_ack, |
busy => i2c_busy, |
al => al, |
din => core_txd, |
dout => core_rxd, |
scl_i => scl_i, |
scl_o => scl_o, |
scl_oen => scl_oen, |
sda_i => sda_i, |
sda_o => sda_o, |
sda_oen => sda_oen |
); |
i2c_al <= al; |
|
-- generate host-command-acknowledge |
cmd_ack <= host_ack; |
|
-- generate go-signal |
go <= (read or write or stop) and not host_ack; |
|
-- assign Dout output to shift-register |
dout <= sr; |
|
-- generate shift register |
shift_register: process(clk, nReset) |
begin |
if (nReset = '0') then |
sr <= (others => '0'); |
elsif (clk'event and clk = '1') then |
if (rst = '1') then |
sr <= (others => '0'); |
elsif (ld = '1') then |
sr <= din; |
elsif (shift = '1') then |
sr <= (sr(6 downto 0) & core_rxd); |
end if; |
end if; |
end process shift_register; |
|
-- generate data-counter |
data_cnt: process(clk, nReset) |
begin |
if (nReset = '0') then |
dcnt <= (others => '0'); |
elsif (clk'event and clk = '1') then |
if (rst = '1') then |
dcnt <= (others => '0'); |
elsif (ld = '1') then |
dcnt <= (others => '1'); -- load counter with 7 |
elsif (shift = '1') then |
dcnt <= dcnt -1; |
end if; |
end if; |
end process data_cnt; |
|
cnt_done <= '1' when (dcnt = 0) else '0'; |
|
-- |
-- state machine |
-- |
statemachine : block |
type states is (st_idle, st_start, st_read, st_write, st_ack, st_stop); |
signal c_state : states; |
begin |
-- |
-- command interpreter, translate complex commands into simpler I2C commands |
-- |
nxt_state_decoder: process(clk, nReset) |
begin |
if (nReset = '0') then |
core_cmd <= I2C_CMD_NOP; |
core_txd <= '0'; |
shift <= '0'; |
ld <= '0'; |
host_ack <= '0'; |
c_state <= st_idle; |
ack_out <= '0'; |
elsif (clk'event and clk = '1') then |
if (rst = '1' or al = '1') then |
core_cmd <= I2C_CMD_NOP; |
core_txd <= '0'; |
shift <= '0'; |
ld <= '0'; |
host_ack <= '0'; |
c_state <= st_idle; |
ack_out <= '0'; |
else |
-- initialy reset all signal |
core_txd <= sr(7); |
shift <= '0'; |
ld <= '0'; |
host_ack <= '0'; |
|
case c_state is |
when st_idle => |
if (go = '1') then |
if (start = '1') then |
c_state <= st_start; |
core_cmd <= I2C_CMD_START; |
elsif (read = '1') then |
c_state <= st_read; |
core_cmd <= I2C_CMD_READ; |
elsif (write = '1') then |
c_state <= st_write; |
core_cmd <= I2C_CMD_WRITE; |
else -- stop |
c_state <= st_stop; |
core_cmd <= I2C_CMD_STOP; |
host_ack <= '1'; -- generate acknowledge signal |
end if; |
|
ld <= '1'; |
end if; |
|
when st_start => |
if (core_ack = '1') then |
if (read = '1') then |
c_state <= st_read; |
core_cmd <= I2C_CMD_READ; |
else |
c_state <= st_write; |
core_cmd <= I2C_CMD_WRITE; |
end if; |
|
ld <= '1'; |
end if; |
|
when st_write => |
if (core_ack = '1') then |
if (cnt_done = '1') then |
c_state <= st_ack; |
core_cmd <= I2C_CMD_READ; |
else |
c_state <= st_write; -- stay in same state |
core_cmd <= I2C_CMD_WRITE; -- write next bit |
shift <= '1'; |
end if; |
end if; |
|
when st_read => |
if (core_ack = '1') then |
if (cnt_done = '1') then |
c_state <= st_ack; |
core_cmd <= I2C_CMD_WRITE; |
else |
c_state <= st_read; -- stay in same state |
core_cmd <= I2C_CMD_READ; -- read next bit |
end if; |
|
shift <= '1'; |
core_txd <= ack_in; |
end if; |
|
when st_ack => |
if (core_ack = '1') then |
-- check for stop; Should a STOP command be generated ? |
if (stop = '1') then |
c_state <= st_stop; |
core_cmd <= I2C_CMD_STOP; |
else |
c_state <= st_idle; |
core_cmd <= I2C_CMD_NOP; |
|
-- generate command acknowledge signal |
host_ack <= '1'; |
end if; |
|
-- assign ack_out output to core_rxd (contains last received bit) |
ack_out <= core_rxd; |
|
core_txd <= '1'; |
else |
core_txd <= ack_in; |
end if; |
|
when st_stop => |
if (core_ack = '1') then |
c_state <= st_idle; |
core_cmd <= I2C_CMD_NOP; |
|
-- generate command acknowledge signal |
host_ack <= '1'; |
end if; |
|
when others => -- illegal states |
c_state <= st_idle; |
core_cmd <= I2C_CMD_NOP; |
report ("Byte controller entered illegal state."); |
|
end case; |
|
end if; |
end if; |
end process nxt_state_decoder; |
|
end block statemachine; |
|
end architecture structural; |
|
/vhdl/i2c_master_top.vhd
0,0 → 1,351
--------------------------------------------------------------------- |
---- ---- |
---- WISHBONE revB2 compl. I2C Master Core; top level ---- |
---- ---- |
---- ---- |
---- Author: Richard Herveille ---- |
---- richard@asics.ws ---- |
---- www.asics.ws ---- |
---- ---- |
---- Downloaded from: http://www.opencores.org/projects/i2c/ ---- |
---- ---- |
--------------------------------------------------------------------- |
---- ---- |
---- Copyright (C) 2000 Richard Herveille ---- |
---- richard@asics.ws ---- |
---- ---- |
---- This source file may be used and distributed without ---- |
---- restriction provided that this copyright statement is not ---- |
---- removed from the file and that any derivative work contains ---- |
---- the original copyright notice and the associated disclaimer.---- |
---- ---- |
---- THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY ---- |
---- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ---- |
---- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ---- |
---- FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR ---- |
---- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ---- |
---- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ---- |
---- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE ---- |
---- GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ---- |
---- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ---- |
---- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ---- |
---- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ---- |
---- OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ---- |
---- POSSIBILITY OF SUCH DAMAGE. ---- |
---- ---- |
--------------------------------------------------------------------- |
|
-- CVS Log |
-- |
-- $Id: i2c_master_top.vhd,v 1.6 2003-08-09 07:01:13 rherveille Exp $ |
-- |
-- $Date: 2003-08-09 07:01:13 $ |
-- $Revision: 1.6 $ |
-- $Author: rherveille $ |
-- $Locker: $ |
-- $State: Exp $ |
-- |
-- Change History: |
-- $Log: not supported by cvs2svn $ |
-- Revision 1.5 2003/02/01 02:03:06 rherveille |
-- Fixed a few 'arbitration lost' bugs. VHDL version only. |
-- |
-- Revision 1.4 2002/12/26 16:05:47 rherveille |
-- Core is now a Multimaster I2C controller. |
-- |
-- Revision 1.3 2002/11/30 22:24:37 rherveille |
-- Cleaned up code |
-- |
-- Revision 1.2 2001/11/10 10:52:44 rherveille |
-- Changed PRER reset value from 0x0000 to 0xffff, conform specs. |
-- |
|
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.std_logic_arith.all; |
|
entity i2c_master_top is |
generic( |
ARST_LVL : std_logic := '0' -- asynchronous reset level |
); |
port ( |
-- wishbone signals |
wb_clk_i : in std_logic; -- master clock input |
wb_rst_i : in std_logic := '0'; -- synchronous active high reset |
arst_i : in std_logic := not ARST_LVL; -- asynchronous reset |
wb_adr_i : in unsigned(2 downto 0); -- lower address bits |
wb_dat_i : in std_logic_vector(7 downto 0); -- Databus input |
wb_dat_o : out std_logic_vector(7 downto 0); -- Databus output |
wb_we_i : in std_logic; -- Write enable input |
wb_stb_i : in std_logic; -- Strobe signals / core select signal |
wb_cyc_i : in std_logic; -- Valid bus cycle input |
wb_ack_o : out std_logic; -- Bus cycle acknowledge output |
wb_inta_o : out std_logic; -- interrupt request output signal |
|
-- i2c lines |
scl_pad_i : in std_logic; -- i2c clock line input |
scl_pad_o : out std_logic; -- i2c clock line output |
scl_padoen_o : out std_logic; -- i2c clock line output enable, active low |
sda_pad_i : in std_logic; -- i2c data line input |
sda_pad_o : out std_logic; -- i2c data line output |
sda_padoen_o : out std_logic -- i2c data line output enable, active low |
); |
end entity i2c_master_top; |
|
architecture structural of i2c_master_top is |
component i2c_master_byte_ctrl is |
port ( |
clk : in std_logic; |
rst : in std_logic; -- synchronous active high reset (WISHBONE compatible) |
nReset : in std_logic; -- asynchornous active low reset (FPGA compatible) |
ena : in std_logic; -- core enable signal |
|
clk_cnt : in unsigned(15 downto 0); -- 4x SCL |
|
-- input signals |
start, |
stop, |
read, |
write, |
ack_in : std_logic; |
din : in std_logic_vector(7 downto 0); |
|
-- output signals |
cmd_ack : out std_logic; |
ack_out : out std_logic; |
i2c_busy : out std_logic; |
i2c_al : out std_logic; |
dout : out std_logic_vector(7 downto 0); |
|
-- i2c lines |
scl_i : in std_logic; -- i2c clock line input |
scl_o : out std_logic; -- i2c clock line output |
scl_oen : out std_logic; -- i2c clock line output enable, active low |
sda_i : in std_logic; -- i2c data line input |
sda_o : out std_logic; -- i2c data line output |
sda_oen : out std_logic -- i2c data line output enable, active low |
); |
end component i2c_master_byte_ctrl; |
|
-- registers |
signal prer : unsigned(15 downto 0); -- clock prescale register |
signal ctr : std_logic_vector(7 downto 0); -- control register |
signal txr : std_logic_vector(7 downto 0); -- transmit register |
signal rxr : std_logic_vector(7 downto 0); -- receive register |
signal cr : std_logic_vector(7 downto 0); -- command register |
signal sr : std_logic_vector(7 downto 0); -- status register |
|
-- internal reset signal |
signal rst_i : std_logic; |
|
-- wishbone write access |
signal wb_wacc : std_logic; |
|
-- internal acknowledge signal |
signal iack_o : std_logic; |
|
-- done signal: command completed, clear command register |
signal done : std_logic; |
|
-- command register signals |
signal sta, sto, rd, wr, ack, iack : std_logic; |
|
signal core_en : std_logic; -- core enable signal |
signal ien : std_logic; -- interrupt enable signal |
|
-- status register signals |
signal irxack, rxack : std_logic; -- received aknowledge from slave |
signal tip : std_logic; -- transfer in progress |
signal irq_flag : std_logic; -- interrupt pending flag |
signal i2c_busy : std_logic; -- i2c bus busy (start signal detected) |
signal i2c_al, al : std_logic; -- arbitration lost |
|
begin |
-- generate internal reset signal |
rst_i <= arst_i xor ARST_LVL; |
|
-- generate acknowledge output signal |
gen_ack_o : process(wb_clk_i) |
begin |
if (wb_clk_i'event and wb_clk_i = '1') then |
iack_o <= wb_cyc_i and wb_stb_i and not iack_o; -- because timing is always honored |
end if; |
end process gen_ack_o; |
wb_ack_o <= iack_o; |
|
|
-- generate wishbone write access signal |
wb_wacc <= wb_cyc_i and wb_stb_i and wb_we_i; |
|
-- assign wb_dat_o |
assign_dato : process(wb_clk_i) |
begin |
if (wb_clk_i'event and wb_clk_i = '1') then |
case wb_adr_i is |
when "000" => wb_dat_o <= std_logic_vector(prer( 7 downto 0)); |
when "001" => wb_dat_o <= std_logic_vector(prer(15 downto 8)); |
when "010" => wb_dat_o <= ctr; |
when "011" => wb_dat_o <= rxr; -- write is transmit register TxR |
when "100" => wb_dat_o <= sr; -- write is command register CR |
|
-- Debugging registers: |
-- These registers are not documented. |
-- Functionality could change in future releases |
when "101" => wb_dat_o <= txr; |
when "110" => wb_dat_o <= cr; |
when "111" => wb_dat_o <= (others => '0'); |
when others => wb_dat_o <= (others => 'X'); -- for simulation only |
end case; |
end if; |
end process assign_dato; |
|
|
-- generate registers (CR, SR see below) |
gen_regs: process(rst_i, wb_clk_i) |
begin |
if (rst_i = '0') then |
prer <= (others => '1'); |
ctr <= (others => '0'); |
txr <= (others => '0'); |
elsif (wb_clk_i'event and wb_clk_i = '1') then |
if (wb_rst_i = '1') then |
prer <= (others => '1'); |
ctr <= (others => '0'); |
txr <= (others => '0'); |
elsif (wb_wacc = '1') then |
case wb_adr_i is |
when "000" => prer( 7 downto 0) <= unsigned(wb_dat_i); |
when "001" => prer(15 downto 8) <= unsigned(wb_dat_i); |
when "010" => ctr <= wb_dat_i; |
when "011" => txr <= wb_dat_i; |
|
-- illegal cases, for simulation only |
when others => |
report ("Illegal write address, setting all registers to unknown."); |
prer <= (others => 'X'); |
ctr <= (others => 'X'); |
txr <= (others => 'X'); |
end case; |
end if; |
end if; |
end process gen_regs; |
|
|
-- generate command register |
gen_cr: process(rst_i, wb_clk_i) |
begin |
if (rst_i = '0') then |
cr <= (others => '0'); |
elsif (wb_clk_i'event and wb_clk_i = '1') then |
if (wb_rst_i = '1') then |
cr <= (others => '0'); |
elsif (wb_wacc = '1') then |
if ( (core_en = '1') and (wb_adr_i = 4) ) then |
-- only take new commands when i2c core enabled |
-- pending commands are finished |
cr <= wb_dat_i; |
end if; |
else |
if (done = '1' or i2c_al = '1') then |
cr(7 downto 4) <= (others => '0'); -- clear command bits when command done or arbitration lost |
end if; |
|
cr(2 downto 1) <= (others => '0'); -- reserved bits, always '0' |
cr(0) <= '0'; -- clear IRQ_ACK bit |
end if; |
end if; |
end process gen_cr; |
|
-- decode command register |
sta <= cr(7); |
sto <= cr(6); |
rd <= cr(5); |
wr <= cr(4); |
ack <= cr(3); |
iack <= cr(0); |
|
-- decode control register |
core_en <= ctr(7); |
ien <= ctr(6); |
|
-- hookup byte controller block |
byte_ctrl: i2c_master_byte_ctrl port map ( |
clk => wb_clk_i, |
rst => wb_rst_i, |
nReset => rst_i, |
ena => core_en, |
clk_cnt => prer, |
start => sta, |
stop => sto, |
read => rd, |
write => wr, |
ack_in => ack, |
i2c_busy => i2c_busy, |
i2c_al => i2c_al, |
din => txr, |
cmd_ack => done, |
ack_out => irxack, |
dout => rxr, |
scl_i => scl_pad_i, |
scl_o => scl_pad_o, |
scl_oen => scl_padoen_o, |
sda_i => sda_pad_i, |
sda_o => sda_pad_o, |
sda_oen => sda_padoen_o |
); |
|
|
-- status register block + interrupt request signal |
st_irq_block : block |
begin |
-- generate status register bits |
gen_sr_bits: process (wb_clk_i, rst_i) |
begin |
if (rst_i = '0') then |
al <= '0'; |
rxack <= '0'; |
tip <= '0'; |
irq_flag <= '0'; |
elsif (wb_clk_i'event and wb_clk_i = '1') then |
if (wb_rst_i = '1') then |
al <= '0'; |
rxack <= '0'; |
tip <= '0'; |
irq_flag <= '0'; |
else |
al <= i2c_al or (al and not sta); |
rxack <= irxack; |
tip <= (rd or wr); |
|
-- interrupt request flag is always generated |
irq_flag <= (done or i2c_al or irq_flag) and not iack; |
end if; |
end if; |
end process gen_sr_bits; |
|
-- generate interrupt request signals |
gen_irq: process (wb_clk_i, rst_i) |
begin |
if (rst_i = '0') then |
wb_inta_o <= '0'; |
elsif (wb_clk_i'event and wb_clk_i = '1') then |
if (wb_rst_i = '1') then |
wb_inta_o <= '0'; |
else |
-- interrupt signal is only generated when IEN (interrupt enable bit) is set |
wb_inta_o <= irq_flag and ien; |
end if; |
end if; |
end process gen_irq; |
|
-- assign status register bits |
sr(7) <= rxack; |
sr(6) <= i2c_busy; |
sr(5) <= al; |
sr(4 downto 2) <= (others => '0'); -- reserved |
sr(1) <= tip; |
sr(0) <= irq_flag; |
end block; |
|
end architecture structural; |
/vhdl/readme
0,0 → 1,25
|
|
-- This code is provided for free and may be used and -- |
-- distributed without restriction provided that the -- |
-- copyright statement is not removed from the file and -- |
-- that any derivative work contains the original -- |
-- copyright notice and the associated disclaimer. -- |
|
-- Comments and suggestions are always welcome -- |
|
The i2c_master core consists of three files: |
|
- i2c_master_top -- top level |
- i2c_master_byte_ctrl -- byte controller |
- i2c_master_bit_ctrl -- bit controller |
|
VHDL needs to be compiled in order. The files are listed |
above in descending order. |
|
I2C.VHD and tst_ds1621.vhd are not supported anymore. |
They remain mostly for historical purposes, altough they |
might prove usefull. |
|
Richard Herveille |
rherveille@opencores.org |
/vhdl/tst_ds1621.vhd
0,0 → 1,283
-- |
-- |
-- State machine for reading data from Dallas 1621 |
-- |
-- Testsystem for i2c controller |
-- |
-- |
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.std_logic_arith.all; |
|
use work.i2c.all; |
|
entity DS1621_interface is |
port ( |
clk : in std_logic; |
nReset : in std_logic; |
|
Dout : out std_logic_vector(7 downto 0); -- data read from ds1621 |
|
error : out std_logic; -- no correct ack received |
|
SCL : inout std_logic; |
SDA : inout std_logic |
); |
end entity DS1621_interface; |
|
architecture structural of DS1621_interface is |
constant SLAVE_ADDR : std_logic_vector(6 downto 0) := "1001000"; |
constant CLK_CNT : unsigned(7 downto 0) := conv_unsigned(20, 8); |
|
signal cmd_ack : std_logic; |
signal D : std_logic_vector(7 downto 0); |
signal lack, store_dout : std_logic; |
|
signal start, read, write, ack, stop : std_logic; |
signal i2c_dout : std_logic_vector(7 downto 0); |
|
begin |
-- hookup I2C controller |
u1: simple_i2c port map (clk => clk, ena => '1', clk_cnt => clk_cnt, nReset => nReset, |
read => read, write => write, start => start, stop => stop, ack_in => ack, cmd_ack => cmd_ack, |
Din => D, Dout => i2c_dout, ack_out => lack, SCL => SCL, SDA => SDA); |
|
init_statemachine : block |
type states is (i1, i2, i3, i4, i5, t1, t2, t3, t4, t5); |
signal state : states; |
begin |
nxt_state_decoder: process(clk, nReset, state) |
variable nxt_state : states; |
variable iD : std_logic_vector(7 downto 0); |
variable ierr : std_logic; |
variable istart, iread, iwrite, iack, istop : std_logic; |
variable istore_dout : std_logic; |
begin |
nxt_state := state; |
ierr := '0'; |
istore_dout := '0'; |
|
istart := start; |
iread := read; |
iwrite := write; |
iack := ack; |
istop := stop; |
iD := D; |
|
case (state) is |
-- init DS1621 |
-- 1) send start condition |
-- 2) send slave address + write |
-- 3) check ack |
-- 4) send "access config" command (0xAC) |
-- 5) check ack |
-- 6) send config register data (0x00) |
-- 7) check ack |
-- 8) send stop condition |
-- 9) send start condition |
-- 10) send slave address + write |
-- 11) check ack |
-- 12) send "start conversion" command (0xEE) |
-- 13) check ack |
-- 14) send stop condition |
|
when i1 => -- send start condition, sent slave address + write |
nxt_state := i2; |
istart := '1'; |
iread := '0'; |
iwrite := '1'; |
iack := '0'; |
istop := '0'; |
iD := (slave_addr & '0'); -- write to slave (R/W = '0') |
|
when i2 => -- send "access config" command |
if (cmd_ack = '1') then |
nxt_state := i3; |
-- check aknowledge bit |
if (lack = '1') then |
ierr := '1'; -- no acknowledge received from last command, expected ACK |
end if; |
|
istart := '0'; |
iread := '0'; |
iwrite := '1'; |
iack := '0'; |
istop := '0'; |
iD := x"AC"; |
end if; |
|
when i3 => -- send config register data, sent stop condition |
if (cmd_ack = '1') then |
nxt_state := i4; |
-- check aknowledge bit |
if (lack = '1') then |
ierr := '1'; -- no acknowledge received from last command, expected ACK |
end if; |
|
istart := '0'; |
iread := '0'; |
iwrite := '1'; |
iack := '0'; |
istop := '1'; |
iD := x"00"; |
end if; |
|
when i4 => -- send start condition, sent slave address + write |
if (cmd_ack = '1') then |
nxt_state := i5; |
|
istart := '1'; |
iread := '0'; |
iwrite := '1'; |
iack := '0'; |
istop := '0'; |
iD := (slave_addr & '0'); -- write to slave (R/W = '0') |
end if; |
|
when i5 => -- send "start conversion" command + stop condition |
if (cmd_ack = '1') then |
nxt_state := t1; |
-- check aknowledge bit |
if (lack = '1') then |
ierr := '1'; -- no acknowledge received from last command, expected ACK |
end if; |
|
istart := '0'; |
iread := '0'; |
iwrite := '1'; |
iack := '0'; |
istop := '1'; |
iD := x"EE"; |
end if; |
-- read temperature |
-- 1) sent start condition |
-- 2) sent slave address + write |
-- 3) check ack |
-- 4) sent "read temperature" command (0xAA) |
-- 5) check ack |
-- 6) sent start condition |
-- 7) sent slave address + read |
-- 8) check ack |
-- 9) read msb |
-- 10) send ack |
-- 11) read lsb |
-- 12) send nack |
-- 13) send stop condition |
|
when t1 => -- send start condition, sent slave address + write |
if (cmd_ack = '1') then |
nxt_state := t2; |
-- check aknowledge bit |
if (lack = '1') then |
ierr := '1'; -- no acknowledge received from last command, expected ACK |
end if; |
|
istart := '1'; |
iread := '0'; |
iwrite := '1'; |
iack := '0'; |
istop := '0'; |
iD := (slave_addr & '0'); -- write to slave (R/W = '0') |
end if; |
|
when t2 => -- send read temperature command |
if (cmd_ack = '1') then |
nxt_state := t3; |
-- check aknowledge bit |
if (lack = '1') then |
ierr := '1'; -- no acknowledge received from last command, expected ACK |
end if; |
|
istart := '0'; |
iread := '0'; |
iwrite := '1'; |
iack := '0'; |
istop := '0'; |
iD := x"AA"; |
end if; |
|
when t3 => -- send (repeated) start condition, send slave address + read |
if (cmd_ack = '1') then |
nxt_state := t4; |
-- check aknowledge bit |
if (lack = '1') then |
ierr := '1'; -- no acknowledge received, expected ACK |
end if; |
|
istart := '1'; |
iread := '0'; |
iwrite := '1'; |
iack := '0'; |
istop := '0'; |
iD := (slave_addr & '1'); -- read from slave (R/W = '1') |
end if; |
|
when t4 => -- read MSB (hi-byte), send acknowledge |
if (cmd_ack = '1') then |
nxt_state := t5; |
-- check aknowledge bit |
if (lack = '1') then |
ierr := '1'; -- no acknowledge received from last command, expected ACK |
end if; |
|
istart := '0'; |
iread := '1'; |
iwrite := '0'; |
iack := '0'; --ACK |
istop := '0'; |
end if; |
|
when t5 => -- read LSB (lo-byte), send acknowledge, sent stop |
if (cmd_ack = '1') then |
nxt_state := t1; |
|
istart := '0'; |
iread := '1'; |
iwrite := '0'; |
iack := '1'; --NACK |
istop := '1'; |
|
istore_dout := '1'; |
end if; |
end case; |
|
-- genregs |
if (nReset = '0') then |
state <= i1; |
error <= '0'; |
store_dout <= '0'; |
|
start <= '0'; |
read <= '0'; |
write <= '0'; |
ack <= '0'; |
stop <= '0'; |
D <= (others => '0'); |
elsif (clk'event and clk = '1') then |
state <= nxt_state; |
error <= ierr; |
store_dout <= istore_dout; |
|
start <= istart; |
read <= iread; |
write <= iwrite; |
ack <= iack; |
stop <= istop; |
D <= iD; |
end if; |
end process nxt_state_decoder; |
end block init_statemachine; |
|
-- store temp |
gen_dout : process(clk) |
begin |
if (clk'event and clk = '1') then |
if (store_dout = '1') then |
Dout <= i2c_dout; |
end if; |
end if; |
end process gen_dout; |
|
end architecture structural; |
|
|
/vhdl/I2C.VHD
0,0 → 1,620
-- |
-- Simple I2C controller |
-- |
-- 1) No multimaster |
-- 2) No slave mode |
-- 3) No fifo's |
-- |
-- notes: |
-- Every command is acknowledged. Do not set a new command before previous is acknowledged. |
-- Dout is available 1 clock cycle later as cmd_ack |
-- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.std_logic_arith.all; |
|
package I2C is |
component simple_i2c is |
port ( |
clk : in std_logic; |
ena : in std_logic; |
nReset : in std_logic; |
|
clk_cnt : in unsigned(7 downto 0); -- 4x SCL |
|
-- input signals |
start, |
stop, |
read, |
write, |
ack_in : std_logic; |
Din : in std_logic_vector(7 downto 0); |
|
-- output signals |
cmd_ack : out std_logic; |
ack_out : out std_logic; |
Dout : out std_logic_vector(7 downto 0); |
|
-- i2c signals |
SCL : inout std_logic; |
SDA : inout std_logic |
); |
end component simple_i2c; |
end package I2C; |
|
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.std_logic_arith.all; |
|
entity simple_i2c is |
port ( |
clk : in std_logic; |
ena : in std_logic; |
nReset : in std_logic; |
|
clk_cnt : in unsigned(7 downto 0); -- 4x SCL |
|
-- input signals |
start, |
stop, |
read, |
write, |
ack_in : std_logic; |
Din : in std_logic_vector(7 downto 0); |
|
-- output signals |
cmd_ack : out std_logic; |
ack_out : out std_logic; |
Dout : out std_logic_vector(7 downto 0); |
|
-- i2c signals |
SCL : inout std_logic; |
SDA : inout std_logic |
); |
end entity simple_i2c; |
|
architecture structural of simple_i2c is |
component i2c_core is |
port ( |
clk : in std_logic; |
nReset : in std_logic; |
|
clk_cnt : in unsigned(7 downto 0); |
|
cmd : in std_logic_vector(2 downto 0); |
cmd_ack : out std_logic; |
busy : out std_logic; |
|
Din : in std_logic; |
Dout : out std_logic; |
|
SCL : inout std_logic; |
SDA : inout std_logic |
); |
end component i2c_core; |
|
-- commands for i2c_core |
constant CMD_NOP : std_logic_vector(2 downto 0) := "000"; |
constant CMD_START : std_logic_vector(2 downto 0) := "010"; |
constant CMD_STOP : std_logic_vector(2 downto 0) := "011"; |
constant CMD_READ : std_logic_vector(2 downto 0) := "100"; |
constant CMD_WRITE : std_logic_vector(2 downto 0) := "101"; |
|
-- signals for i2c_core |
signal core_cmd : std_logic_vector(2 downto 0); |
signal core_ack, core_busy, core_txd, core_rxd : std_logic; |
|
-- signals for shift register |
signal sr : std_logic_vector(7 downto 0); -- 8bit shift register |
signal shift, ld : std_logic; |
|
-- signals for state machine |
signal go, host_ack : std_logic; |
begin |
-- hookup i2c core |
u1: i2c_core port map (clk, nReset, clk_cnt, core_cmd, core_ack, core_busy, core_txd, core_rxd, SCL, SDA); |
|
-- generate host-command-acknowledge |
cmd_ack <= host_ack; |
|
-- generate go-signal |
go <= (read or write) and not host_ack; |
|
-- assign Dout output to shift-register |
Dout <= sr; |
|
-- assign ack_out output to core_rxd (contains last received bit) |
ack_out <= core_rxd; |
|
-- generate shift register |
shift_register: process(clk) |
begin |
if (clk'event and clk = '1') then |
if (ld = '1') then |
sr <= din; |
elsif (shift = '1') then |
sr <= (sr(6 downto 0) & core_rxd); |
end if; |
end if; |
end process shift_register; |
|
-- |
-- state machine |
-- |
statemachine : block |
type states is (st_idle, st_start, st_read, st_write, st_ack, st_stop); |
signal state : states; |
signal dcnt : unsigned(2 downto 0); |
begin |
-- |
-- command interpreter, translate complex commands into simpler I2C commands |
-- |
nxt_state_decoder: process(clk, nReset, state) |
variable nxt_state : states; |
variable idcnt : unsigned(2 downto 0); |
variable ihost_ack : std_logic; |
variable icore_cmd : std_logic_vector(2 downto 0); |
variable icore_txd : std_logic; |
variable ishift, iload : std_logic; |
begin |
-- 8 databits (1byte) of data to shift-in/out |
idcnt := dcnt; |
|
-- no acknowledge (until command complete) |
ihost_ack := '0'; |
|
icore_txd := core_txd; |
|
-- keep current command to i2c_core |
icore_cmd := core_cmd; |
|
-- no shifting or loading of shift-register |
ishift := '0'; |
iload := '0'; |
|
-- keep current state; |
nxt_state := state; |
case state is |
when st_idle => |
if (go = '1') then |
if (start = '1') then |
nxt_state := st_start; |
icore_cmd := CMD_START; |
elsif (read = '1') then |
nxt_state := st_read; |
icore_cmd := CMD_READ; |
idcnt := "111"; |
else |
nxt_state := st_write; |
icore_cmd := CMD_WRITE; |
idcnt := "111"; |
iload := '1'; |
end if; |
end if; |
|
when st_start => |
if (core_ack = '1') then |
if (read = '1') then |
nxt_state := st_read; |
icore_cmd := CMD_READ; |
idcnt := "111"; |
else |
nxt_state := st_write; |
icore_cmd := CMD_WRITE; |
idcnt := "111"; |
iload := '1'; |
end if; |
end if; |
|
when st_write => |
if (core_ack = '1') then |
idcnt := dcnt -1; -- count down Data_counter |
icore_txd := sr(7); |
if (dcnt = 0) then |
nxt_state := st_ack; |
icore_cmd := CMD_READ; |
else |
ishift := '1'; |
-- icore_txd := sr(7); |
end if; |
end if; |
|
when st_read => |
if (core_ack = '1') then |
idcnt := dcnt -1; -- count down Data_counter |
ishift := '1'; |
if (dcnt = 0) then |
nxt_state := st_ack; |
icore_cmd := CMD_WRITE; |
icore_txd := ack_in; |
end if; |
end if; |
|
when st_ack => |
if (core_ack = '1') then |
-- generate command acknowledge signal |
ihost_ack := '1'; |
|
-- Perform an additional shift, needed for 'read' (store last received bit in shift register) |
ishift := '1'; |
|
-- check for stop; Should a STOP command be generated ? |
if (stop = '1') then |
nxt_state := st_stop; |
icore_cmd := CMD_STOP; |
else |
nxt_state := st_idle; |
icore_cmd := CMD_NOP; |
end if; |
end if; |
|
when st_stop => |
if (core_ack = '1') then |
nxt_state := st_idle; |
icore_cmd := CMD_NOP; |
end if; |
|
when others => -- illegal states |
nxt_state := st_idle; |
icore_cmd := CMD_NOP; |
end case; |
|
-- generate registers |
if (nReset = '0') then |
core_cmd <= CMD_NOP; |
core_txd <= '0'; |
|
shift <= '0'; |
ld <= '0'; |
|
dcnt <= "111"; |
host_ack <= '0'; |
|
state <= st_idle; |
elsif (clk'event and clk = '1') then |
if (ena = '1') then |
state <= nxt_state; |
|
dcnt <= idcnt; |
shift <= ishift; |
ld <= iload; |
|
core_cmd <= icore_cmd; |
core_txd <= icore_txd; |
|
host_ack <= ihost_ack; |
end if; |
end if; |
end process nxt_state_decoder; |
|
end block statemachine; |
|
end architecture structural; |
|
|
-- |
-- |
-- I2C Core |
-- |
-- Translate simple commands into SCL/SDA transitions |
-- Each command has 5 states, A/B/C/D/idle |
-- |
-- start: SCL ~~~~~~~~~~\____ |
-- SDA ~~~~~~~~\______ |
-- x | A | B | C | D | i |
-- |
-- repstart SCL ____/~~~~\___ |
-- SDA __/~~~\______ |
-- x | A | B | C | D | i |
-- |
-- stop SCL ____/~~~~~~~~ |
-- SDA ==\____/~~~~~ |
-- x | A | B | C | D | i |
-- |
--- write SCL ____/~~~~\____ |
-- SDA ==X=========X= |
-- x | A | B | C | D | i |
-- |
--- read SCL ____/~~~~\____ |
-- SDA XXXX=====XXXX |
-- x | A | B | C | D | i |
-- |
|
-- Timing: Normal mode Fast mode |
----------------------------------------------------------------- |
-- Fscl 100KHz 400KHz |
-- Th_scl 4.0us 0.6us High period of SCL |
-- Tl_scl 4.7us 1.3us Low period of SCL |
-- Tsu:sta 4.7us 0.6us setup time for a repeated start condition |
-- Tsu:sto 4.0us 0.6us setup time for a stop conditon |
-- Tbuf 4.7us 1.3us Bus free time between a stop and start condition |
-- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.std_logic_arith.all; |
|
entity i2c_core is |
port ( |
clk : in std_logic; |
nReset : in std_logic; |
|
clk_cnt : in unsigned(7 downto 0); |
|
cmd : in std_logic_vector(2 downto 0); |
cmd_ack : out std_logic; |
busy : out std_logic; |
|
Din : in std_logic; |
Dout : out std_logic; |
|
SCL : inout std_logic; |
SDA : inout std_logic |
); |
end entity i2c_core; |
|
architecture structural of i2c_core is |
constant CMD_NOP : std_logic_vector(2 downto 0) := "000"; |
constant CMD_START : std_logic_vector(2 downto 0) := "010"; |
constant CMD_STOP : std_logic_vector(2 downto 0) := "011"; |
constant CMD_READ : std_logic_vector(2 downto 0) := "100"; |
constant CMD_WRITE : std_logic_vector(2 downto 0) := "101"; |
|
type cmds is (idle, start_a, start_b, start_c, start_d, stop_a, stop_b, stop_c, rd_a, rd_b, rd_c, rd_d, wr_a, wr_b, wr_c, wr_d); |
signal state : cmds; |
signal SDAo, SCLo : std_logic; |
signal txd : std_logic; |
signal clk_en, slave_wait :std_logic; |
signal cnt : unsigned(7 downto 0) := clk_cnt; |
begin |
-- whenever the slave is not ready it can delay the cycle by pulling SCL low |
slave_wait <= '1' when ((SCLo = '1') and (SCL = '0')) else '0'; |
|
-- generate clk enable signal |
gen_clken: process(clk, nReset) |
begin |
if (nReset = '0') then |
cnt <= (others => '0'); |
clk_en <= '1'; --'0'; |
elsif (clk'event and clk = '1') then |
if (cnt = 0) then |
clk_en <= '1'; |
cnt <= clk_cnt; |
else |
if (slave_wait = '0') then |
cnt <= cnt -1; |
end if; |
clk_en <= '0'; |
end if; |
end if; |
end process gen_clken; |
|
-- generate statemachine |
nxt_state_decoder : process (clk, nReset, state, cmd, SDA) |
variable nxt_state : cmds; |
variable icmd_ack, ibusy, store_sda : std_logic; |
variable itxd : std_logic; |
begin |
|
nxt_state := state; |
|
icmd_ack := '0'; -- default no acknowledge |
ibusy := '1'; -- default busy |
|
store_sda := '0'; |
|
itxd := txd; |
|
case (state) is |
-- idle |
when idle => |
case cmd is |
when CMD_START => |
nxt_state := start_a; |
icmd_ack := '1'; -- command completed |
|
when CMD_STOP => |
nxt_state := stop_a; |
icmd_ack := '1'; -- command completed |
|
when CMD_WRITE => |
nxt_state := wr_a; |
icmd_ack := '1'; -- command completed |
itxd := Din; |
|
when CMD_READ => |
nxt_state := rd_a; |
icmd_ack := '1'; -- command completed |
|
when others => |
nxt_state := idle; |
-- don't acknowledge NOP command icmd_ack := '1'; -- command completed |
ibusy := '0'; |
end case; |
|
-- start |
when start_a => |
nxt_state := start_b; |
|
when start_b => |
nxt_state := start_c; |
|
when start_c => |
nxt_state := start_d; |
|
when start_d => |
nxt_state := idle; |
ibusy := '0'; -- not busy when idle |
|
|
-- stop |
when stop_a => |
nxt_state := stop_b; |
|
when stop_b => |
nxt_state := stop_c; |
|
when stop_c => |
-- nxt_state := stop_d; |
|
-- when stop_d => |
nxt_state := idle; |
ibusy := '0'; -- not busy when idle |
|
-- read |
when rd_a => |
nxt_state := rd_b; |
|
when rd_b => |
nxt_state := rd_c; |
|
when rd_c => |
nxt_state := rd_d; |
store_sda := '1'; |
|
when rd_d => |
nxt_state := idle; |
ibusy := '0'; -- not busy when idle |
|
-- write |
when wr_a => |
nxt_state := wr_b; |
|
when wr_b => |
nxt_state := wr_c; |
|
when wr_c => |
nxt_state := wr_d; |
|
when wr_d => |
nxt_state := idle; |
ibusy := '0'; -- not busy when idle |
|
end case; |
|
-- generate regs |
if (nReset = '0') then |
state <= idle; |
cmd_ack <= '0'; |
busy <= '0'; |
txd <= '0'; |
Dout <= '0'; |
elsif (clk'event and clk = '1') then |
if (clk_en = '1') then |
state <= nxt_state; |
busy <= ibusy; |
|
txd <= itxd; |
if (store_sda = '1') then |
Dout <= SDA; |
end if; |
end if; |
|
cmd_ack <= icmd_ack and clk_en; |
end if; |
end process nxt_state_decoder; |
|
-- |
-- convert states to SCL and SDA signals |
-- |
output_decoder: process (clk, nReset, state) |
variable iscl, isda : std_logic; |
begin |
case (state) is |
when idle => |
iscl := SCLo; -- keep SCL in same state |
isda := SDA; -- keep SDA in same state |
|
-- start |
when start_a => |
iscl := SCLo; -- keep SCL in same state (for repeated start) |
isda := '1'; -- set SDA high |
|
when start_b => |
iscl := '1'; -- set SCL high |
isda := '1'; -- keep SDA high |
|
when start_c => |
iscl := '1'; -- keep SCL high |
isda := '0'; -- sel SDA low |
|
when start_d => |
iscl := '0'; -- set SCL low |
isda := '0'; -- keep SDA low |
|
-- stop |
when stop_a => |
iscl := '0'; -- keep SCL disabled |
isda := '0'; -- set SDA low |
|
when stop_b => |
iscl := '1'; -- set SCL high |
isda := '0'; -- keep SDA low |
|
when stop_c => |
iscl := '1'; -- keep SCL high |
isda := '1'; -- set SDA high |
|
-- write |
when wr_a => |
iscl := '0'; -- keep SCL low |
-- isda := txd; -- set SDA |
isda := Din; |
|
when wr_b => |
iscl := '1'; -- set SCL high |
-- isda := txd; -- set SDA |
isda := Din; |
|
when wr_c => |
iscl := '1'; -- keep SCL high |
-- isda := txd; -- set SDA |
isda := Din; |
|
when wr_d => |
iscl := '0'; -- set SCL low |
-- isda := txd; -- set SDA |
isda := Din; |
|
-- read |
when rd_a => |
iscl := '0'; -- keep SCL low |
isda := '1'; -- tri-state SDA |
|
when rd_b => |
iscl := '1'; -- set SCL high |
isda := '1'; -- tri-state SDA |
|
when rd_c => |
iscl := '1'; -- keep SCL high |
isda := '1'; -- tri-state SDA |
|
when rd_d => |
iscl := '0'; -- set SCL low |
isda := '1'; -- tri-state SDA |
end case; |
|
-- generate registers |
if (nReset = '0') then |
SCLo <= '1'; |
SDAo <= '1'; |
elsif (clk'event and clk = '1') then |
if (clk_en = '1') then |
SCLo <= iscl; |
SDAo <= isda; |
end if; |
end if; |
end process output_decoder; |
|
SCL <= '0' when (SCLo = '0') else 'Z'; -- since SCL is externally pulled-up convert a '1' to a 'Z'(tri-state) |
SDA <= '0' when (SDAo = '0') else 'Z'; -- since SDA is externally pulled-up convert a '1' to a 'Z'(tri-state) |
-- SCL <= SCLo; |
-- SDA <= SDAo; |
|
end architecture structural; |
|
|
|
|