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

Subversion Repositories i2c

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 9 to Rev 10
    Reverse comparison

Rev 9 → Rev 10

/trunk/vhdl/I2C.VHD File deleted
/trunk/documentation/I2C_specs.doc Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream
trunk/documentation/I2C_specs.doc Property changes : Deleted: svn:mime-type ## -1 +0,0 ## -application/octet-stream \ No newline at end of property Index: trunk/documentation/i2c_rev03.pdf =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: trunk/documentation/i2c_rev03.pdf =================================================================== --- trunk/documentation/i2c_rev03.pdf (revision 9) +++ trunk/documentation/i2c_rev03.pdf (nonexistent)
trunk/documentation/i2c_rev03.pdf Property changes : Deleted: svn:mime-type ## -1 +0,0 ## -application/octet-stream \ No newline at end of property Index: trunk/bench/verilog/tst_bench_top.v =================================================================== --- trunk/bench/verilog/tst_bench_top.v (nonexistent) +++ trunk/bench/verilog/tst_bench_top.v (revision 10) @@ -0,0 +1,270 @@ +// +// Testbench for wishbone i2c master module +// + +`include "timescale.v" + +module tst_bench_top(); + + // + // wires && regs + // + reg clk; + reg rstn; + + wire [31:0] adr; + wire [ 7:0] dat_i, dat_o; + wire we; + wire stb; + wire cyc; + wire ack; + wire inta; + + reg [7:0] q; + + wire scl, scl_o, scl_oen; + wire sda, sda_o, sda_oen; + + parameter PRER_LO = 3'b000; + parameter PRER_HI = 3'b001; + parameter CTR = 3'b010; + parameter RXR = 3'b011; + parameter TXR = 3'b011; + parameter CR = 3'b100; + parameter SR = 3'b100; + + parameter TXR_R = 3'b101; // undocumented / reserved output + parameter CR_R = 3'b110; // undocumented / reserved output + + // + // Module body + // + + // generate clock + always #5 clk = ~clk; + + // hookup wishbone master model + wb_master_model u0 ( + .clk(clk), + .rst(rstn), + .adr(adr), + .din(dat_i), + .dout(dat_o), + .cyc(cyc), + .stb(stb), + .we(we), + .ack(ack), + .err(1'b0), + .rty(1'b0) + ); + + // hookup wishbone_i2c_master core + i2c_master_top i2c_top ( + + // wishbone interface + .wb_clk_i(clk), + .wb_rst_i(1'b0), + .arst_i(rstn), + .wb_adr_i(adr[2:0]), + .wb_dat_i(dat_o), + .wb_dat_o(dat_i), + .wb_we_i(we), + .wb_stb_i(stb), + .wb_cyc_i(cyc), + .wb_ack_o(ack), + .wb_inta_o(inta), + + // i2c signals + .scl_pad_i(scl), + .scl_pad_o(scl_o), + .scl_padoen_o(scl_oen), + .sda_pad_i(sda), + .sda_pad_o(sda_o), + .sda_padoen_o(sda_oen) + ); + + // hookup i2c slave model + i2c_slave_model #(7'b1010_000) i2c_slave ( + .scl(scl), + .sda(sda) + ); + + // create i2c lines + assign scl = scl_oen ? 1'bz : scl_o; // create tri-state buffer for i2c_master scl line + assign sda = sda_oen ? 1'bz : sda_o; // create tri-state buffer for i2c_master sda line + + pullup p1(scl); // pullup scl line + pullup p2(sda); // pullup sda line + + initial + begin + // initially values + clk = 0; + + // reset system + rstn = 1'b1; // negate reset + #2; + rstn = 1'b0; // assert reset + repeat(20) @(posedge clk); + rstn = 1'b1; // negate reset + + @(posedge clk); + + // + // program core + // + + // program internal registers + u0.wb_write(1, PRER_LO, 8'hfa); // load prescaler lo-byte + u0.wb_write(1, PRER_HI, 8'h00); // load prescaler hi-byte + + u0.wb_write(1, CTR, 8'h80); // enable core + + // + // access slave (write) + // + + // drive slave address + u0.wb_write(1, TXR, 8'ha0); // present slave address, set write-bit (== !read) + u0.wb_write(0, CR, 8'h90); // set command (start, write) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(0, SR, q); // poll it until it is zero + + // send memory address + u0.wb_write(1, TXR, 8'h01); // present slave's memory address + u0.wb_write(0, CR, 8'h10); // set command (write) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(0, SR, q); // poll it until it is zero + + // send memory contents + u0.wb_write(1, TXR, 8'ha5); // present data + u0.wb_write(0, CR, 8'h10); // set command (stop, write) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + + // send memory contents for next memory address (auto_inc) + u0.wb_write(1, TXR, 8'h5a); // present data + u0.wb_write(0, CR, 8'h50); // set command (stop, write) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + + + // + // delay + // + #100000; // wait for 10us. + + // + // access slave (read) + // + + // drive slave address + u0.wb_write(1, TXR, 8'ha0); // present slave address, set write-bit (== !read) + u0.wb_write(0, CR, 8'h90); // set command (start, write) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + + // send memory address + u0.wb_write(1, TXR, 8'h00); // present slave's memory address + u0.wb_write(0, CR, 8'h10); // set command (write) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + + // drive slave address + u0.wb_write(1, TXR, 8'ha1); // present slave's address, set read-bit + u0.wb_write(0, CR, 8'h90); // set command (start, write) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + + // read data from slave + u0.wb_write(1, CR, 8'h20); // set command (read, ack_read) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + + // read data from slave + u0.wb_write(1, CR, 8'h20); // set command (read, ack_read) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + + // read data from slave + u0.wb_write(1, CR, 8'h20); // set command (read, ack_read) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + + // read data from slave + u0.wb_write(1, CR, 8'h28); // set command (read, nack_read) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + + // + // check invalid slave memory address + // + + // drive slave address + u0.wb_write(1, TXR, 8'ha0); // present slave address, set write-bit (== !read) + u0.wb_write(0, CR, 8'h90); // set command (start, write) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + + // send memory address + u0.wb_write(1, TXR, 8'h10); // present slave's memory address + u0.wb_write(0, CR, 8'h10); // set command (write) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + + // slave should have send NACK + if (!q[7]) + $display("\nERROR: Expected NACK, received ACK\n"); + + // read data from slave + u0.wb_write(1, CR, 8'h40); // set command (stop) + + // check tip bit + u0.wb_read(1, SR, q); + while (q[1]) + u0.wb_read(1, SR, q); // poll it until it is zero + + end + +endmodule + + Index: trunk/bench/verilog/wb_master_model.v =================================================================== --- trunk/bench/verilog/wb_master_model.v (nonexistent) +++ trunk/bench/verilog/wb_master_model.v (revision 10) @@ -0,0 +1,114 @@ +// +// Wishbone master model +// + +`include "timescale.v" + +module wb_master_model(clk, rst, adr, din, dout, cyc, stb, we, ack, err, rty); + +input clk, rst; +output [31:0] adr; +input [ 7:0] din; +output [ 7:0] dout; +output cyc, stb; +output we; +input ack, err, rty; + +//////////////////////////////////////////////////////////////////// +// +// Local Wires +// + +reg [31:0] adr; +reg [ 7:0] dout; +reg cyc, stb; +reg we; + +//////////////////////////////////////////////////////////////////// +// +// Memory Logic +// + +initial + begin + //adr = 32'hxxxx_xxxx; + //adr = 0; + adr = 32'hxxxx_xxxx; + dout = 8'hxx; + cyc = 1'b0; + stb = 1'bx; + we = 1'hx; + #1; + $display("\nINFO: WISHBONE MASTER MODEL INSTANTIATED (%m)\n"); + end + +//////////////////////////////////////////////////////////////////// +// +// Wishbone write cycle +// + +task wb_write; + input delay; + integer delay; + + input [31:0] a; + input [ 7:0] d; + + begin + + repeat(delay) @(posedge clk); + #1; + adr = a; + dout = d; + cyc = 1'b1; + stb = 1'b1; + we = 1'b1; + + @(posedge clk); + while(~ack) @(posedge clk); + #1; + cyc = 1'b0; + stb = 1'bx; + adr = 32'hxxxx_xxxx; + dout = 8'hxx; + we = 1'hx; + + end +endtask + +//////////////////////////////////////////////////////////////////// +// +// Wishbone read cycle +// + +task wb_read; + input delay; + integer delay; + + input [31:0] a; + output [ 7:0] d; + + begin + + repeat(delay) @(posedge clk); + #1; + adr = a; + dout = 8'hxx; + cyc = 1'b1; + stb = 1'b1; + we = 1'b0; + + @(posedge clk); + while(~ack) @(posedge clk); + #1; + cyc = 1'b0; + stb = 1'bx; + adr = 32'hxxxx_xxxx; + dout = 8'hxx; + we = 1'hx; + d = din; + + end +endtask + +endmodule Index: trunk/bench/verilog/i2c_slave_model.v =================================================================== --- trunk/bench/verilog/i2c_slave_model.v (nonexistent) +++ trunk/bench/verilog/i2c_slave_model.v (revision 10) @@ -0,0 +1,209 @@ +// +// I2C slave model +// + +`include "timescale.v" + +module i2c_slave_model (scl, sda); + + // + // parameters + // + parameter I2C_ADR = 7'b001_0000; + + // + // input && outpus + // + input scl; + inout sda; + + // + // Variable declaration + // + reg [7:0] mem [3:0]; // initiate memory + reg [7:0] mem_adr; // memory address + reg [7:0] mem_do; // memory data output + + reg sta, d_sta; + reg sto, d_sto; + + reg [7:0] sr; // 8bit shift register + reg rw; // read/write direction + + wire my_adr; // my address called ?? + wire i2c_reset; // i2c-statemachine reset + reg [2:0] bit_cnt; // 3bit downcounter + wire acc_done; // 8bits transfered + reg ld; // load downcounter + + reg sda_o; // sda-drive level + + // statemachine declaration + parameter idle = 3'b000; + parameter slave_ack = 3'b001; + parameter get_mem_adr = 3'b010; + parameter gma_ack = 3'b011; + parameter data = 3'b100; + parameter data_ack = 3'b101; + + reg [2:0] state; // synopsys enum_state + + // + // module body + // + + initial + begin + sda_o = 1'b1; + state = idle; + end + + // generate shift register + always@(posedge scl) + sr <= #1 {sr[6:0],sda}; + + //detect my_address + assign my_adr = (sr[7:1] == I2C_ADR); + + //generate bit-counter + always@(posedge scl) + if (ld) + bit_cnt <= #1 3'b111; + else + bit_cnt <= #1 bit_cnt - 3'h1; + + //generate access done signal + assign acc_done = !(|bit_cnt); + + //detect start condition + always@(negedge sda) + if (scl) + sta <= #1 1'b1; + else + sta <= #1 1'b0; + + always@(posedge scl) + d_sta <= #1 sta; + + // detect stop condition + always@(posedge sda) + if (scl) + sto <= #1 1'b1; + else + sto <= #1 1'b0; + + //generate i2c_reset signal + assign i2c_reset = sta || sto; + + // generate statemachine + always@(negedge scl or posedge sto) + if (sto || (sta && !d_sta) ) + begin + state <= #1 idle; // reset statemachine + + sda_o <= #1 1'b1; + ld <= #1 1'b1; + end + else + begin + // initial settings + sda_o <= #1 1'b1; + ld <= #1 1'b0; + + case (state) // synopsys full_case parallel_case + idle: // idle state + if (acc_done && my_adr) + begin + state <= #1 slave_ack; + rw <= #1 sr[0]; + + sda_o <= #1 1'b0; // generate i2c_ack + end + + slave_ack: + begin + if (rw) + begin + state <= #1 data; + sda_o <= #1 mem_do[7]; + end + else + state <= #1 get_mem_adr; + + ld <= #1 1'b1; + end + + get_mem_adr: // wait for memory address + if (acc_done) + begin + state <= #1 gma_ack; + mem_adr <= #1 sr; // store memory address + + sda_o <= #1 !(sr <= 15); // generate i2c_ack, for valid address + end + + gma_ack: + begin + state <= #1 data; + ld <= #1 1'b1; + end + + data: // receive or drive data + begin + if (rw) + sda_o <= #1 mem_do[7]; + + if (acc_done) + begin + state <= #1 data_ack; + + mem_adr <= #1 mem_adr + 8'h1; + + if (!rw) + mem[ mem_adr[3:0] ] <= #1 sr; // store data in memory + + sda_o <= #1 (rw && (mem_adr <= 15) ); // send ack on write, receive ack on read + end + end + + data_ack: + begin + ld <= #1 1'b1; + + if (rw) + if (sda) // read operation && master send NACK + begin + state <= #1 idle; + sda_o <= #1 1'b1; + end + else + begin + state <= #1 data; + sda_o <= #1 mem_do[7]; + end + else + begin + state <= #1 data; + sda_o <= #1 1'b1; + end + end + + endcase + end + + // read data from memory + always@(posedge scl) + if (acc_done) + mem_do <= #1 mem[mem_adr]; + else + mem_do <= #1 {mem_do[6:0], 1'b1}; // insert 1'b1 for host ack generation + + // generate tri-states + assign sda = sda_o ? 1'bz : 1'b0; + +endmodule + + + + + Index: trunk/rtl/verilog/i2c_master_byte_ctrl.v =================================================================== --- trunk/rtl/verilog/i2c_master_byte_ctrl.v (nonexistent) +++ trunk/rtl/verilog/i2c_master_byte_ctrl.v (revision 10) @@ -0,0 +1,284 @@ +// +// WISHBONE revB2 compiant I2C master core +// +// author: Richard Herveille +// rev. 0.1 August 24th, 2001. Initial Verilog release. +// + +`include "timescale.v" +`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, 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 [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 [3: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), + .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 4'h0; + else if (rst) + dcnt <= #1 4'h0; + else if (ld) + dcnt <= #1 4'h7; + else if (shift) + dcnt <= #1 dcnt - 4'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 sr[7]; + + shift <= #1 1'b0; + ld <= #1 1'b0; + + cmd_ack <= #1 1'b0; + c_state <= #1 ST_IDLE; + end + else if (rst) + 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; + 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; + 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; + end + + // assign ack_out output to bit_controller_rxd (contains last received bit) + ack_out = 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; + end + + endcase + end +endmodule + + Index: trunk/rtl/verilog/i2c_master_defines.v =================================================================== --- trunk/rtl/verilog/i2c_master_defines.v (nonexistent) +++ trunk/rtl/verilog/i2c_master_defines.v (revision 10) @@ -0,0 +1,14 @@ +// 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 + + +// asynchronous reset level +// I2C_RST_LVL == 1'b0 asynchronous active low reset +// I2C_RST_LVL == 1'b1 asynchronous active high reset +`define I2C_RST_LVL 1'b0 \ No newline at end of file Index: trunk/rtl/verilog/timescale.v =================================================================== --- trunk/rtl/verilog/timescale.v (nonexistent) +++ trunk/rtl/verilog/timescale.v (revision 10) @@ -0,0 +1,2 @@ +`timescale 1ns / 10ps + Index: trunk/rtl/verilog/i2c_master_bit_ctrl.v =================================================================== --- trunk/rtl/verilog/i2c_master_bit_ctrl.v (nonexistent) +++ trunk/rtl/verilog/i2c_master_bit_ctrl.v (revision 10) @@ -0,0 +1,436 @@ +// +// WISHBONE revB2 compiant I2C master core, bit controller +// +// author: Richard Herveille +// rev. 0.1 August 19th, 2001. Initial Verilog release. +// + + +// +///////////////////////////////////// +// 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 +// + +`include "timescale.v" +`include "i2c_master_defines.v" + +module i2c_master_bit_ctrl(clk, rst, nReset, clk_cnt, ena, cmd, cmd_ack, busy, 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; + reg cmd_ack; + output busy; + reg busy; + + 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 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 + // + + // synchronize SCL and SDA inputs + always@(posedge clk) + begin + sSCL <= #1 scl_i; + sSDA <= #1 sda_i; + end + + // whenever the slave is not ready it can delay the cycle by pulling SCL low + assign slave_wait = scl_oen && !sSCL; + + // generate clk enable signal + always@(posedge clk or negedge nReset) + if (!nReset) + begin + cnt <= #1 15'h0; + clk_en <= #1 1'b1; + end + else if (rst) + begin + cnt <= #1 15'h0; + clk_en <= #1 1'b1; + end + else if ( !(|cnt) || !ena) + begin + cnt <= #1 clk_cnt; + clk_en <= #1 1'b1; + end + else + begin + if (!slave_wait) + cnt <= #1 cnt - 1'h1; + + clk_en <= #1 1'b0; + end + + + // generate bus status controller + reg dSDA; + reg sta_condition; + reg sto_condition; + + // 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) + begin + dSDA <= #1 sSDA; // generate a delayed versio nof sSDA + + sta_condition <= #1 !sSDA && dSDA && sSCL; + sto_condition <= #1 sSDA && !dSDA && sSCL; + end + + // generate bus busy signal + always@(posedge clk or negedge nReset) + if (!nReset) + busy <= #1 1'b0; + else if (rst) + busy <= #1 1'b0; + else + busy <= (sta_condition || busy) && !sto_condition; + + + // generate statemachine + + // nxt_state decoder + parameter [14:0] idle = 15'b000_0000_0000_0000; + parameter [14:0] start_a = 15'b000_0000_0000_0001; + parameter [14:0] start_b = 15'b000_0000_0000_0010; + parameter [14:0] start_c = 15'b000_0000_0000_0100; + parameter [14:0] start_d = 15'b000_0000_0000_1000; + parameter [14:0] stop_a = 15'b000_0000_0001_0000; + parameter [14:0] stop_b = 15'b000_0000_0010_0000; + parameter [14:0] stop_c = 15'b000_0000_0100_0000; + parameter [14:0] rd_a = 15'b000_0000_1000_0000; + parameter [14:0] rd_b = 15'b000_0001_0000_0000; + parameter [14:0] rd_c = 15'b000_0010_0000_0000; + parameter [14:0] rd_d = 15'b000_0100_0000_0000; + parameter [14:0] wr_a = 15'b000_1000_0000_0000; + parameter [14:0] wr_b = 15'b001_0000_0000_0000; + parameter [14:0] wr_c = 15'b010_0000_0000_0000; + parameter [14:0] wr_d = 15'b100_0000_0000_0000; + + reg [14:0] c_state, nxt_state; // synopsis enum_state + reg icmd_ack, store_sda; + + always@(c_state or cmd) + begin + nxt_state = c_state; + icmd_ack = 1'b0; // default no command acknowledge + store_sda = 1'b0; + + case (c_state) // synopsis full_case parallel_case + // idle state + idle: + case (cmd) // synopsis full_case parallel_case + `I2C_CMD_START: + nxt_state = start_a; + + `I2C_CMD_STOP: + nxt_state = stop_a; + + `I2C_CMD_WRITE: + nxt_state = wr_a; + + `I2C_CMD_READ: + nxt_state = rd_a; + + default: + nxt_state = idle; + + endcase + + // start + start_a: + nxt_state = start_b; + + start_b: + nxt_state = start_c; + + start_c: + nxt_state = start_d; + + start_d: + begin + nxt_state = idle; + icmd_ack = 1'b1; + end + + // stop + stop_a: + nxt_state = stop_b; + + stop_b: + nxt_state = stop_c; + + stop_c: + begin + nxt_state = idle; + icmd_ack = 1'b1; + end + + // read + rd_a: + nxt_state = rd_b; + + rd_b: + nxt_state = rd_c; + + rd_c: + begin + nxt_state = rd_d; + store_sda = 1'b1; + end + + rd_d: + begin + nxt_state = idle; + icmd_ack = 1'b1; + end + + // write + wr_a: + nxt_state = wr_b; + + wr_b: + nxt_state = wr_c; + + wr_c: + nxt_state = wr_d; + + wr_d: + begin + nxt_state = idle; + icmd_ack = 1'b1; + end + + endcase + end + + + // generate registers + always@(posedge clk or negedge nReset) + if (!nReset) + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b0; + dout <= #1 1'b0; + end + else if (rst) + begin + c_state <= #1 idle; + cmd_ack <= #1 1'b0; + dout <= #1 1'b0; + end + else + begin + if (clk_en) + begin + c_state <= #1 nxt_state; + if(store_sda) + dout <= #1 sSDA; + end + + cmd_ack <= #1 icmd_ack && clk_en; + end + + // + // convert states to SCL and SDA signals + // + + // assign scl and sda output (always gnd) + assign scl_o = 1'b0; + assign sda_o = 1'b0; + + // assign scl and sda output_enables + always@(posedge clk or negedge nReset) + if (!nReset) + begin + scl_oen <= #1 1'b1; + sda_oen <= #1 1'b1; + end + else if (rst) + begin + scl_oen <= #1 1'b1; + sda_oen <= #1 1'b1; + end + else if (clk_en) + case (c_state) // synopsis full_case parallel_case + + // idle state + idle: + begin + scl_oen <= #1 scl_oen; // keep SCL in same state + sda_oen <= #1 sda_oen; // keep SDA in same state + end + + // start + start_a: + begin + scl_oen <= #1 scl_oen; // keep SCL in same state + sda_oen <= #1 1'b1; // set SDA high + end + + start_b: + begin + scl_oen <= #1 1'b1; // set SCL high + sda_oen <= #1 1'b1; // keep SDA high + end + + start_c: + begin + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b0; // set SDA low + end + + start_d: + begin + scl_oen <= #1 1'b0; // set SCL low + sda_oen <= #1 1'b0; // keep SDA low + end + + // stop + stop_a: + begin + scl_oen <= #1 1'b0; // keep SCL low + sda_oen <= #1 1'b0; // set SDA low + end + + stop_b: + begin + scl_oen <= #1 1'b1; // set SCL high + sda_oen <= #1 1'b0; // keep SDA low + end + + stop_c: + begin + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b1; // set SDA high + end + + //write + wr_a: + begin + scl_oen <= #1 1'b0; // keep SCL low + sda_oen <= #1 din; // set SDA + end + + wr_b: + begin + scl_oen <= #1 1'b1; // set SCL high + sda_oen <= #1 din; // keep SDA + end + + wr_c: + begin + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 din; + end + + wr_d: + begin + scl_oen <= #1 1'b0; // set SCL low + sda_oen <= #1 din; + end + + // read + rd_a: + begin + scl_oen <= #1 1'b0; // keep SCL low + sda_oen <= #1 1'b1; // tri-state SDA + end + + rd_b: + begin + scl_oen <= #1 1'b1; // set SCL high + sda_oen <= #1 1'b1; // keep SDA tri-stated + end + + rd_c: + begin + scl_oen <= #1 1'b1; // keep SCL high + sda_oen <= #1 1'b1; + end + + rd_d: + begin + scl_oen <= #1 1'b0; // set SCL low + sda_oen <= #1 1'b1; + end + + endcase + +endmodule + + + + + + + Index: trunk/rtl/verilog/i2c_master_top.v =================================================================== --- trunk/rtl/verilog/i2c_master_top.v (nonexistent) +++ trunk/rtl/verilog/i2c_master_top.v (revision 10) @@ -0,0 +1,218 @@ +// +// WISHBONE revB2 compiant I2C master core +// +// author: Richard Herveille +// rev. 0.1 26-08-2001. Iinitial Verilog release +// + +`include "timescale.v" +`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 ); + + // + // 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 + reg [7:0] wb_dat_o; + 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 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; + + // 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) + + // + // module body + // + + // generate internal reset + wire rst_i = arst_i ^ `I2C_RST_LVL; + + // generate acknowledge output signal + assign wb_ack_o = wb_cyc_i && wb_stb_i; // because timing is always honored + + // assign DAT_O + always@(wb_adr_i or prer or ctr or txr or cr or rxr or sr) + begin + case (wb_adr_i) // synopsis full_case parallel_case + 3'b000: wb_dat_o = prer[ 7:0]; + 3'b001: wb_dat_o = prer[15:8]; + 3'b010: wb_dat_o = ctr; + 3'b011: wb_dat_o = rxr; // write is transmit register (txr) + 3'b100: wb_dat_o = sr; // write is command register (cr) + 3'b101: wb_dat_o = txr; + 3'b110: wb_dat_o = cr; + endcase + end + + + // generate registers + always@(posedge wb_clk_i or negedge rst_i) + if (!rst_i) + begin + prer <= #1 16'h0; + ctr <= #1 8'h0; + txr <= #1 8'h0; + cr <= #1 8'h0; + end + else if (wb_rst_i) + begin + prer <= #1 16'h0; + ctr <= #1 8'h0; + txr <= #1 8'h0; + cr <= #1 8'h0; + end + else + if (wb_cyc_i && wb_stb_i && wb_we_i) + begin + if (!wb_adr_i[2]) + case (wb_adr_i[1:0]) // synopsis full_case parallel_case + 2'b00 : prer [ 7:0] <= #1 wb_dat_i; + 2'b01 : prer [15:8] <= #1 wb_dat_i; + 2'b10 : ctr <= #1 wb_dat_i; + 2'b11 : txr <= #1 wb_dat_i; + endcase + else + if (core_en && (wb_adr_i[1:0] == 2'b00) ) // only take new commands when i2c core enabled, pending commands are finished + cr <= #1 wb_dat_i; + end + else + begin + if (done) + cr[7:4] <= #1 4'h0; // clear command bits when done + + cr[2:1] <= #1 2'b00; // reserved bits + cr[0] <= #1 cr[0] && irq_flag; // clear when irq_flag cleared + 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]; + + // 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), + .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 + rxack <= #1 1'b0; + tip <= #1 1'b0; + irq_flag <= #1 1'b0; + end + else if (wb_rst_i) + begin + rxack <= #1 1'b0; + tip <= #1 1'b0; + irq_flag <= #1 1'b0; + end + else + begin + rxack <= #1 irxack; + tip <= #1 (rd || wr); + irq_flag <= #1 (done || 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 && ctr[6]; // 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:2] = 4'h0; // reserved + assign sr[1] = tip; + assign sr[0] = irq_flag; + +endmodule + + + + + + + + Index: trunk/rtl/vhdl/wishbone_i2c_master.vhd =================================================================== --- trunk/rtl/vhdl/wishbone_i2c_master.vhd (nonexistent) +++ trunk/rtl/vhdl/wishbone_i2c_master.vhd (revision 10) @@ -0,0 +1,943 @@ +-- +-- WISHBONE revB2 compiant I2C master core +-- +-- author: Richard Herveille +-- rev. 0.1 based on simple_i2c +-- rev. 0.2 april 27th 2001, fixed incomplete sensitivity list on assign_dato process (thanks to Matt Oseman) +-- rev. 0.3 may 4th 2001, fixed typo rev.0.2 txt -> txr +-- rev. 0.4 may 8th, added some remarks, fixed some sensitivity list issues +-- +-- +-- Changes compared to simple_i2c +-- 1) WISHBONE interface +-- 2) added start/stop detection +-- 3) added busy bit +-- 4) removed automatic tri-state buffer insertion (for ASIC support) +-- + + + +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_arith.all; + +entity wishbone_i2c_master is + port ( + -- wishbone signals + CLK_I : in std_logic; -- master clock input + RST_I : in std_logic := '0'; -- synchronous active high reset + nRESET: in std_logic := '1'; -- asynchronous active low reset + ADR_I : in unsigned(1 downto 0); -- lower address bits + DAT_I : in std_logic_vector(15 downto 0); -- Databus input + DAT_O : out std_logic_vector(15 downto 0); -- Databus output + SEL_I : in std_logic_vector(1 downto 0); -- Byte select signals + WE_I : in std_logic; -- Write enable input + STB_I : in std_logic; -- Strobe signals / core select signal + CYC_I : in std_logic; -- Valid bus cycle input + ACK_O : out std_logic; -- Bus cycle acknowledge output + INTA_O : out std_logic; -- interrupt request output signal + + -- I2C signals + SCLi : in std_logic; -- I2C clock line + SCLo : out std_logic; + SDAi : in std_logic; -- I2C data line + SDAo : out std_logic + ); +end entity wishbone_i2c_master; + +architecture structural of wishbone_i2c_master is + component 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) + + clk_cnt : in unsigned(15 downto 0); -- 4x SCL + + -- input signals + ena, + 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_busy : out std_logic; + + -- i2c signals + SCLi : in std_logic; -- I2C clock line + SCLo : out std_logic; + SDAi : in std_logic; -- I2C data line + SDAo : out std_logic + ); + end component 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 + + -- done signal: command completed, clear command register + signal done : std_logic; + + -- command register signals + signal sta, sto, rd, wr, ack, iack : std_logic; + + -- core enable signal + signal core_en : std_logic; + + -- 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; -- bus busy (start signal detected) + +begin + -- generate acknowledge output signal + ACK_O <= STB_I; -- since timing is always honored + + + -- assign DAT_O + assign_dato : process(ADR_I, prer, ctr, txr, cr, rxr, sr) + begin + case ADR_I is + when "00" => + DAT_O <= std_logic_vector(prer); + + when "01" => + DAT_O <= (x"00" & ctr); + + when "10" => + DAT_O <= (txr & cr); + + when "11" => + DAT_O <= (rxr & sr); + + when others => + DAT_O <= (others => 'X'); -- for simulation only + end case; + end process assign_dato; + + + -- registers block + regs_block: block + -- address decode signals + signal we_a0, we_a1, we_a2, we_a3 : std_logic; + begin + -- decode address lines + we_a0 <= CYC_I and STB_I and WE_I and not ADR_I(1) and not ADR_I(0); + we_a1 <= CYC_I and STB_I and WE_I and not ADR_I(1) and ADR_I(0); + we_a2 <= CYC_I and STB_I and WE_I and ADR_I(1) and not ADR_I(0); + we_a3 <= CYC_I and STB_I and WE_I and ADR_I(1) and ADR_I(0); + + -- store data in writeable registers + -- prescale register + write_prer: process(nRESET, CLK_I) + begin + if (nRESET = '0') then + prer <= (others => '1'); + elsif (CLK_I'event and CLK_I = '1') then + if (RST_I = '1') then + prer <= (others => '1'); + else + if ( (we_a0 and SEL_I(1)) = '1') then + prer(15 downto 8) <= unsigned(DAT_I(15 downto 8)); + end if; + + if ( (we_a0 and SEL_I(0)) = '1') then + prer(7 downto 0) <= unsigned(DAT_I(7 downto 0)); + end if; + end if; + end if; + end process write_prer; + + + -- control register + write_ctr: process(nRESET, CLK_I) + begin + if (nRESET = '0') then + ctr <= (others => '0'); + elsif (CLK_I'event and CLK_I = '1') then + if (RST_I = '1') then + ctr <= (others => '0'); + else + if ( (we_a1 and SEL_I(0)) = '1') then + ctr <= DAT_I(7 downto 0); + end if; + end if; + end if; + end process write_ctr; + + -- transmit register + write_txr: process(nRESET, CLK_I) + begin + if (nRESET = '0') then + txr <= (others => '0'); + elsif (CLK_I'event and CLK_I = '1') then + if (RST_I = '1') then + txr <= (others => '0'); + else + if ( (we_a2 and SEL_I(1)) = '1') then + txr <= DAT_I(15 downto 8); + end if; + end if; + end if; + end process write_txr; + + + -- command register + write_cr: process(nRESET, CLK_I) + begin + if (nRESET = '0') then + cr <= (others => '0'); -- asynchronous clear + elsif (CLK_I'event and CLK_I = '1') then + if (RST_I = '1') then + cr <= (others => '0'); -- synchronous clear + else + + if ( (we_a2 and SEL_I(0)) = '1') then + if (core_en = '1') then + cr <= DAT_I(7 downto 0); -- only take new commands when I2C core is enabled, pending commands are finished + end if; + else + if (done = '0') then + cr(7 downto 4) <= cr(7 downto 4); + else + cr(7 downto 0) <= (others => '0'); -- clear command_bits when command completed + end if; + cr(2 downto 1) <= cr(2 downto 1); + cr(0) <= cr(0) and irq_flag; -- automatically clear when irq_flag is cleared + end if; + end if; + end if; + end process write_cr; + end block regs_block; + + + -- 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); + + -- hookup byte controller block + u1: byte_ctrl port map (clk => CLK_I, rst => RST_I, nReset => nRESET, clk_cnt => prer, ena => core_en, + start => sta, stop => sto, read => rd, write => wr, ack_in => ack, i2c_busy => i2c_busy, + Din => txr, cmd_ack => done, ack_out => irxack, Dout => rxr, -- note: maybe store rxr in registers ?? + SCLi => SCLi, SCLo => SCLo, SDAi => SDAi, SDAo => SDAo); + + + -- status register block + interrupt request signal + st_block : block + begin + -- generate status register bits + gen_sr_bits: process (CLK_I, nRESET) + begin + if (nRESET = '0') then + rxack <= '0'; + tip <= '0'; + irq_flag <= '0'; + elsif (CLK_I'event and CLK_I = '1') then + if (RST_I = '1') then + rxack <= '0'; + tip <= '0'; + irq_flag <= '0'; + else + rxack <= irxack; + tip <= ( rd or wr ); + irq_flag <= (done or irq_flag) and not iack; -- interrupt request flag is always generated + end if; + end if; + end process gen_sr_bits; + + -- generate interrupt request signals + gen_irq: process (CLK_I, nRESET) + begin + if (nRESET = '0') then + INTA_O <= '0'; + elsif (CLK_I'event and CLK_I = '1') then + if (RST_I = '1') then + INTA_O <= '0'; + else + INTA_O <= irq_flag and ctr(6); -- interrupt signal is only generated when IEN (interrupt enable bit) is set + end if; + end if; + end process gen_irq; + + -- assign status register bits + sr(7) <= rxack; + sr(6) <= i2c_busy; + sr(5 downto 2) <= (others => '0'); -- reserved + sr(1) <= tip; + sr(0) <= irq_flag; + end block; + +end architecture structural; + + +-- +------------------------------------------ +-- Byte controller section +------------------------------------------ +-- +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_arith.all; + +entity 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) + + clk_cnt : in unsigned(15 downto 0); -- 4x SCL + + -- input signals + ena, + 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_busy : out std_logic; + + -- i2c signals + SCLi : in std_logic; -- I2C clock line + SCLo : out std_logic; + SDAi : in std_logic; -- I2C data line + SDAo : out std_logic + ); +end entity byte_ctrl; + +architecture structural of byte_ctrl is + component bit_ctrl is + port ( + clk : in std_logic; + rst : in std_logic; + nReset : in std_logic; + + clk_cnt : in unsigned(15 downto 0); -- clock prescale value + + ena : in std_logic; -- core enable signal + 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; + + SCLin : in std_logic; -- I2C clock line + SCLout : out std_logic; + SDAin : in std_logic; -- I2C data line + SDAout : out std_logic + ); + end component bit_ctrl; + + -- 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 bit_controller + signal core_cmd : std_logic_vector(2 downto 0); + signal core_ack, 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 bit_controller + u1: bit_ctrl port map (clk, rst, nReset, clk_cnt, ena, core_cmd, core_ack, i2c_busy, core_txd, core_rxd, SCLi, SCLo, SDAi, SDAo); + + -- 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 bit_controller + 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 (rst = '1') then + core_cmd <= CMD_NOP; + core_txd <= '0'; + + shift <= '0'; + ld <= '0'; + + dcnt <= "111"; + host_ack <= '0'; + + state <= st_idle; + else + 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; + + +-- +------------------------------------- +-- 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 +-- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_arith.all; + +entity bit_ctrl is + port ( + clk : in std_logic; + rst : in std_logic; + nReset : in std_logic; + + clk_cnt : in unsigned(15 downto 0); -- clock prescale value + + ena : in std_logic; -- core enable signal + 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; + + -- i2c lines + SCLin : in std_logic; -- I2C clock line + SCLout : out std_logic; + SDAin : in std_logic; -- I2C data line + SDAout : out std_logic + ); +end entity bit_ctrl; + +architecture structural of bit_ctrl 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 SCLo, SDAo : std_logic; -- internal I2C lines + signal sSCL, sSDA : std_logic; -- synchronized SCL and SDA inputs + + signal txd : std_logic; -- transmit bit + signal clk_en, slave_wait :std_logic; -- clock generation signals +-- signal cnt : unsigned(15 downto 0) := clk_cnt; -- clock divider counter (simulation) + signal cnt : unsigned(15 downto 0); -- clock divider counter (synthesis) +begin + -- synchronize SCL and SDA inputs + synch_SCL_SDA: process(clk) + begin + if (clk'event and clk = '1') then + sSCL <= SCLin; + sSDA <= SDAin; + end if; + end process synch_SCL_SDA; + + -- whenever the slave is not ready it can delay the cycle by pulling SCL low + slave_wait <= '1' when ( (SCLo = '1') and (sSCL = '0') ) else '0'; + + -- 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 + 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 if; + end process gen_clken; + + + -- generate bus status controller + bus_status_ctrl: block + signal dSDA : std_logic; + signal sta_condition : std_logic; + signal sto_condition : std_logic; + + signal ibusy : std_logic; + begin + -- detect start condition => detect falling edge on SDA while SCL is high + -- detect stop condition => detect rising edge on SDA while SCL is high + det_sta_sto: process(clk) + begin + if (clk'event and clk = '1') then + dSDA <= sSDA; -- generate a delayed version of sSDA + + sta_condition <= (not sSDA and dSDA) and sSCL; + sto_condition <= (sSDA and not dSDA) and sSCL; + end if; + end process det_sta_sto; + + -- generate 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; + + -- assign output + busy <= ibusy; + end block bus_status_ctrl; + + + -- generate statemachine + nxt_state_decoder : process (clk, nReset, state, cmd) + variable nxt_state : cmds; + variable icmd_ack, store_sda : std_logic; + variable itxd : std_logic; + begin + + nxt_state := state; + + icmd_ack := '0'; -- default no acknowledge + + 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 + 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; + + -- stop + when stop_a => + nxt_state := stop_b; + + when stop_b => + nxt_state := stop_c; + + when stop_c => + nxt_state := 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; + + -- 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; + + end case; + + -- generate regs + if (nReset = '0') then + state <= idle; + cmd_ack <= '0'; + txd <= '0'; + Dout <= '0'; + elsif (clk'event and clk = '1') then + if (rst = '1') then + state <= idle; + cmd_ack <= '0'; + txd <= '0'; + Dout <= '0'; + else + if (clk_en = '1') then + state <= nxt_state; + + txd <= itxd; + if (store_sda = '1') then + Dout <= sSDA; + end if; + end if; + + cmd_ack <= icmd_ack and clk_en; + end if; + 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 := sSDA; -- 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 := Din; + + when wr_b => + iscl := '1'; -- set SCL high + isda := Din; + + when wr_c => + iscl := '1'; -- keep SCL high + isda := Din; + + when wr_d => + iscl := '0'; -- set SCL low + 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 (rst = '1') then + SCLo <= '1'; + SDAo <= '1'; + else + if (clk_en = '1') then + SCLo <= iscl; + SDAo <= isda; + end if; + end if; + end if; + end process output_decoder; + + -- assign outputs + SCLout <= SCLo; + SDAout <= SDAo; +end architecture structural; + Index: trunk/rtl/vhdl/tst_ds1621.vhd =================================================================== --- trunk/rtl/vhdl/tst_ds1621.vhd (nonexistent) +++ trunk/rtl/vhdl/tst_ds1621.vhd (revision 10) @@ -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; + + Index: trunk/rtl/vhdl/I2C.VHD =================================================================== --- trunk/rtl/vhdl/I2C.VHD (nonexistent) +++ trunk/rtl/vhdl/I2C.VHD (revision 10) @@ -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; + + + + Index: trunk/doc/i2c_rev03.pdf =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: trunk/doc/i2c_rev03.pdf =================================================================== --- trunk/doc/i2c_rev03.pdf (nonexistent) +++ trunk/doc/i2c_rev03.pdf (revision 10)
trunk/doc/i2c_rev03.pdf Property changes : Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: trunk/doc/src/I2C_specs.doc =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: trunk/doc/src/I2C_specs.doc =================================================================== --- trunk/doc/src/I2C_specs.doc (nonexistent) +++ trunk/doc/src/I2C_specs.doc (revision 10)
trunk/doc/src/I2C_specs.doc Property changes : Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property

powered by: WebSVN 2.1.0

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