diff --git a/i2c_slave_opencores/.gitignore b/i2c_slave_opencores/.gitignore new file mode 100644 index 0000000..4ee54fa --- /dev/null +++ b/i2c_slave_opencores/.gitignore @@ -0,0 +1,4 @@ +*.vcd +sim/*.vcd +sim/*.wlf +sim/testHarness diff --git a/i2c_slave_opencores/README.md b/i2c_slave_opencores/README.md new file mode 100644 index 0000000..95bff49 --- /dev/null +++ b/i2c_slave_opencores/README.md @@ -0,0 +1,7 @@ +# i2c_slave_opencores + +I2C Verilog Testbench für einen CO2 Sensor + +Beispiel ist von OpenCores + +https://git.efi.th-nuernberg.de/gitea/schmidtsi76327/i2c_slave_opencores \ No newline at end of file diff --git a/i2c_slave_opencores/bench/i2cSlaveTB_defines.v b/i2c_slave_opencores/bench/i2cSlaveTB_defines.v new file mode 100644 index 0000000..31ae560 --- /dev/null +++ b/i2c_slave_opencores/bench/i2cSlaveTB_defines.v @@ -0,0 +1,17 @@ +// ---------------------------- i2cSlaveTB_defines.v ----------------- +`define SEND_START 1'b1 +`define SEND_STOP 1'b1 +`define NULL 1'b0 +`define ACK 1'b0 +`define NACK 1'b1 + +`define DEV_I2C_ADDR 8'hcc + +`define PRER_LO_REG 3'b000 +`define PRER_HI_REG 3'b001 +`define CTR_REG 3'b010 +`define RXR_REG 3'b011 +`define TXR_REG 3'b011 +`define CR_REG 3'b100 +`define SR_REG 3'b100 + diff --git a/i2c_slave_opencores/bench/multiByteReadWrite.v b/i2c_slave_opencores/bench/multiByteReadWrite.v new file mode 100644 index 0000000..3048bfd --- /dev/null +++ b/i2c_slave_opencores/bench/multiByteReadWrite.v @@ -0,0 +1,168 @@ +// ------------------ multiByteReadWrite.v ---------------------- +`include "timescale.v" +`include "i2cSlaveTB_defines.v" + + +module multiByteReadWrite(); +reg ack; +reg [31:0] readData; +reg [7:0] dataByteRead; +//reg [7:0] dataMSB; + +// ------------------ write ---------------------- +task write; +input [7:0] i2cAddr; +input [15:0] regAddr; +input [31:0] data; +input stop; + +begin + $write("I2C Write: At [0x%0x] = 0x%0x\n", regAddr, data); + + //i2c address + testHarness.u_wb_master_model.wb_write(1, `TXR_REG, i2cAddr); + testHarness.u_wb_master_model.wb_write(1, `CR_REG , 8'h90); //STA, WR + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("I2C device address sent, SR = 0x%x\n", dataByteRead ); + + //slave reg address high byte + testHarness.u_wb_master_model.wb_write(1, `TXR_REG, regAddr[15:8]); + testHarness.u_wb_master_model.wb_write(1, `CR_REG , 8'h10); //WR + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("Slave reg address sent, SR = 0x%x\n", dataByteRead ); + + //slave reg address low byte + testHarness.u_wb_master_model.wb_write(1, `TXR_REG, regAddr[7:0]); + testHarness.u_wb_master_model.wb_write(1, `CR_REG , 8'h10); //WR + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("Slave reg address sent, SR = 0x%x\n", dataByteRead ); + + //data[31:24] + testHarness.u_wb_master_model.wb_write(1, `TXR_REG, data[31:24]); + testHarness.u_wb_master_model.wb_write(1, `CR_REG , 8'h10); //WR + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("Data[31:24] sent, SR = 0x%x\n", dataByteRead ); + + //data[23:16] + testHarness.u_wb_master_model.wb_write(1, `TXR_REG, data[23:16]); + testHarness.u_wb_master_model.wb_write(1, `CR_REG , 8'h10); //WR + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("Data[23:16] sent, SR = 0x%x\n", dataByteRead ); + + //data[15:8] + testHarness.u_wb_master_model.wb_write(1, `TXR_REG, data[15:8]); + testHarness.u_wb_master_model.wb_write(1, `CR_REG , 8'h10); //WR + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("Data[15:8] sent, SR = 0x%x\n", dataByteRead ); + + //data[7:0] + testHarness.u_wb_master_model.wb_write(1, `TXR_REG, data[7:0]); + testHarness.u_wb_master_model.wb_write(1, `CR_REG , {1'b0, stop, 6'b010000}); //STO?, WR + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("Data[7:0] sent, SR = 0x%x\n", dataByteRead ); + +end +endtask + +// ------------------ read ---------------------- +task read; +input [7:0] i2cAddr; +input [15:0] regAddr; +input [31:0] expectedData; +output [31:0] data; +input stop; + +begin + + //i2c address + testHarness.u_wb_master_model.wb_write(1, `TXR_REG, i2cAddr); //write + testHarness.u_wb_master_model.wb_write(1, `CR_REG , 8'h90); //STA, WR + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("I2C device address sent, SR = 0x%x\n", dataByteRead ); + #5000; + + //slave reg address high byte + testHarness.u_wb_master_model.wb_write(1, `TXR_REG, regAddr[15:8]); + testHarness.u_wb_master_model.wb_write(1, `CR_REG , {1'b0, stop, 6'b010000}); //STO?, WR + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("Slave reg address sent, SR = 0x%x\n", dataByteRead ); + #5000; + //slave reg address low byte + testHarness.u_wb_master_model.wb_write(1, `TXR_REG, regAddr[7:0]); + testHarness.u_wb_master_model.wb_write(1, `CR_REG , {1'b0, stop, 6'b010000}); //STO?, WR + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("Slave reg address sent, SR = 0x%x\n", dataByteRead ); + #5000; + + //i2c address + testHarness.u_wb_master_model.wb_write(1, `TXR_REG, {i2cAddr[7:1], 1'b1}); //read + testHarness.u_wb_master_model.wb_write(1, `CR_REG , 8'h90); //STA, WR + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("I2C device address sent, SR = 0x%x\n", dataByteRead ); + + //data[31:24] + testHarness.u_wb_master_model.wb_write(1, `CR_REG , 8'h20); //RD, ACK + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("Data[31:24] rxed, SR = 0x%x\n", dataByteRead ); + testHarness.u_wb_master_model.wb_read(1, `RXR_REG, readData[31:24]); + + //data[23:16] + testHarness.u_wb_master_model.wb_write(1, `CR_REG , 8'h20); //RD, ACK + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("Data[23:16] rxed, SR = 0x%x\n", dataByteRead ); + testHarness.u_wb_master_model.wb_read(1, `RXR_REG, readData[23:16]); + + //data[15:8] + testHarness.u_wb_master_model.wb_write(1, `CR_REG , 8'h20); //RD, ACK + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("Data[15:8] rxed, SR = 0x%x\n", dataByteRead ); + testHarness.u_wb_master_model.wb_read(1, `RXR_REG, readData[15:8]); + + //data[7:0] + testHarness.u_wb_master_model.wb_write(1, `CR_REG , {1'b0, 1'b0, 6'b101000}); //STO, RD, NAK + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + while (dataByteRead[1] == 1'b1) //while trans in progress + testHarness.u_wb_master_model.wb_read(1, `SR_REG , dataByteRead); + //$write("Data[7:0] rxed, SR = 0x%x\n", dataByteRead ); + testHarness.u_wb_master_model.wb_read(1, `RXR_REG, readData[7:0]); + + data = readData; + if (data != expectedData) begin + $write("***** I2C Read ERROR: At 0x%0x. Expected 0x%0x, got 0x%0x\n", regAddr, expectedData, data); + //$stop; + end + else + $write("[read] I2C Read: At [0x%x] = 0x%0x\n", regAddr, data); +end +endtask + +endmodule + diff --git a/i2c_slave_opencores/bench/testCase0.v b/i2c_slave_opencores/bench/testCase0.v new file mode 100644 index 0000000..dd6b5b9 --- /dev/null +++ b/i2c_slave_opencores/bench/testCase0.v @@ -0,0 +1,57 @@ +// ---------------------------------- testcase0.v ---------------------------- +`include "timescale.v" +`include "i2cSlave_define.v" +`include "i2cSlaveTB_defines.v" + +module testCase0(); + +reg ack; +reg [7:0] data; +reg [15:0] dataWord; +reg [7:0] dataRead; +reg [7:0] dataWrite; +integer i; +integer j; + +initial +begin + $write("\n\n"); + testHarness.reset; + testHarness.tb_readEn <= 1'b0; + testHarness.tb_writeEn <= 1'b0; + + // set i2c master clock scale reg PRER = (48MHz / (5 * 400KHz) ) - 1 + $write("Testing register read/write\n"); + testHarness.u_wb_master_model.wb_write(1, `PRER_LO_REG , 8'h17); + testHarness.u_wb_master_model.wb_write(1, `PRER_HI_REG , 8'h00); + testHarness.u_wb_master_model.wb_cmp(1, `PRER_LO_REG , 8'h17); + + // enable i2c master + testHarness.u_wb_master_model.wb_write(1, `CTR_REG , 8'h80); + + + multiByteReadWrite.write({`I2C_ADDRESS, 1'b0}, 16'h1234, 32'h89abcdef, `SEND_STOP); + multiByteReadWrite.read({`I2C_ADDRESS, 1'b0}, 16'h1234, 32'h89abcdef, dataWord, `NULL); + #100; + + testHarness.tb_addr <= 16'h1234; + testHarness.tb_dataIn <= 16'h5555; + + #10 testHarness.tb_writeEn <= 1'b1; + #10 testHarness.tb_readEn <= 1'b1; + #10 testHarness.tb_writeEn <= 1'b0; + #10 testHarness.tb_readEn <= 1'b0; + + #100; + multiByteReadWrite.read({`I2C_ADDRESS, 1'b0}, 16'h1234, 32'h89abcdef, dataWord, `NULL); + + #100; + multiByteReadWrite.write({`I2C_ADDRESS, 1'b0}, 16'h1234, 32'h89abcdef, `SEND_STOP); + multiByteReadWrite.read({`I2C_ADDRESS, 1'b0}, 16'h1234, 32'h89abcdef, dataWord, `NULL); + + $write("Finished all tests\n"); + $finish; + +end +endmodule + diff --git a/i2c_slave_opencores/bench/testHarness.v b/i2c_slave_opencores/bench/testHarness.v new file mode 100644 index 0000000..2b3e06c --- /dev/null +++ b/i2c_slave_opencores/bench/testHarness.v @@ -0,0 +1,122 @@ +// -------------------------- testHarness.v ----------------------- +`include "timescale.v" + +module testHarness (); + +reg rst; +reg clk; +reg i2cHostClk; +wire sda; +wire scl; +wire sdaOutEn; +wire sdaOut; +wire sdaIn; +wire [2:0] adr; +wire [7:0] masterDout; +wire [7:0] masterDin; +wire we; +wire stb; +wire cyc; +wire ack; +wire scl_pad_i; +wire scl_pad_o; +wire scl_padoen_o; +wire sda_pad_i; +wire sda_pad_o; +wire sda_padoen_o; + +// tb passthrough +reg tb_readEn; +reg tb_writeEn; +reg [15:0] tb_addr; +reg [15:0] tb_dataIn; +wire [15:0] tb_dataOut; + +initial begin +$dumpfile("wave.vcd"); +$dumpvars(0, testHarness); +end + +i2cSlave u_i2cSlave( + .clk(clk), + .rst(rst), + .sda(sda), + .scl(scl), + .tb_readEn(tb_readEn), + .tb_writeEn(tb_writeEn), + .tb_addr(tb_addr), + .tb_dataIn(tb_dataIn), + .tb_dataOut(tb_dataOut) +); + +i2c_master_top #(.ARST_LVL(1'b1)) u_i2c_master_top ( + .wb_clk_i(clk), + .wb_rst_i(rst), + .arst_i(rst), + .wb_adr_i(adr), + .wb_dat_i(masterDout), + .wb_dat_o(masterDin), + .wb_we_i(we), + .wb_stb_i(stb), + .wb_cyc_i(cyc), + .wb_ack_o(ack), + .wb_inta_o(), + .scl_pad_i(scl_pad_i), + .scl_pad_o(scl_pad_o), + .scl_padoen_o(scl_padoen_o), + .sda_pad_i(sda_pad_i), + .sda_pad_o(sda_pad_o), + .sda_padoen_o(sda_padoen_o) +); + +wb_master_model #(.dwidth(8), .awidth(3)) u_wb_master_model ( + .clk(clk), + .rst(rst), + .adr(adr), + .din(masterDin), + .dout(masterDout), + .cyc(cyc), + .stb(stb), + .we(we), + .sel(), + .ack(ack), + .err(1'b0), + .rty(1'b0) +); + +assign sda = (sda_padoen_o == 1'b0) ? sda_pad_o : 1'bz; +assign sda_pad_i = sda; +pullup(sda); + +assign scl = (scl_padoen_o == 1'b0) ? scl_pad_o : 1'bz; +assign scl_pad_i = scl; +pullup(scl); + + +// ****************************** Clock section ****************************** +//approx 48MHz clock +`define CLK_HALF_PERIOD 10 +always begin + #`CLK_HALF_PERIOD clk <= 1'b0; + #`CLK_HALF_PERIOD clk <= 1'b1; +end + + +// ****************************** reset ****************************** +task reset; +begin + rst <= 1'b1; + @(posedge clk); + @(posedge clk); + @(posedge clk); + @(posedge clk); + @(posedge clk); + @(posedge clk); + rst <= 1'b0; + @(posedge clk); + @(posedge clk); + @(posedge clk); +end +endtask + +endmodule diff --git a/i2c_slave_opencores/doc/Philips_I2C_spec.pdf b/i2c_slave_opencores/doc/Philips_I2C_spec.pdf new file mode 100644 index 0000000..84424c9 Binary files /dev/null and b/i2c_slave_opencores/doc/Philips_I2C_spec.pdf differ diff --git a/i2c_slave_opencores/doc/i2cSlave_FSM.pdf b/i2c_slave_opencores/doc/i2cSlave_FSM.pdf new file mode 100644 index 0000000..f9b88fc Binary files /dev/null and b/i2c_slave_opencores/doc/i2cSlave_FSM.pdf differ diff --git a/i2c_slave_opencores/doc/i2cSlave_IPCore_Specification.pdf b/i2c_slave_opencores/doc/i2cSlave_IPCore_Specification.pdf new file mode 100644 index 0000000..28cbafc Binary files /dev/null and b/i2c_slave_opencores/doc/i2cSlave_IPCore_Specification.pdf differ diff --git a/i2c_slave_opencores/doc/i2c_master_specs.pdf b/i2c_slave_opencores/doc/i2c_master_specs.pdf new file mode 100644 index 0000000..9476f10 Binary files /dev/null and b/i2c_slave_opencores/doc/i2c_master_specs.pdf differ diff --git a/i2c_slave_opencores/doc/src/i2cSlave_IPCore_Specification.sxw b/i2c_slave_opencores/doc/src/i2cSlave_IPCore_Specification.sxw new file mode 100644 index 0000000..f1d0c0a Binary files /dev/null and b/i2c_slave_opencores/doc/src/i2cSlave_IPCore_Specification.sxw differ diff --git a/i2c_slave_opencores/model/i2c_master_bit_ctrl.v b/i2c_slave_opencores/model/i2c_master_bit_ctrl.v new file mode 100644 index 0000000..ccac6d1 --- /dev/null +++ b/i2c_slave_opencores/model/i2c_master_bit_ctrl.v @@ -0,0 +1,538 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// 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.1 2008-11-08 13:15:10 sfielding Exp $ +// +// $Date: 2008-11-08 13:15:10 $ +// $Revision: 1.1 $ +// $Author: sfielding $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: not supported by cvs2svn $ +// Revision 1.12 2006/09/04 09:08:13 rherveille +// fixed short scl high pulse after clock stretch +// fixed slave model not returning correct '(n)ack' signal +// +// Revision 1.11 2004/05/07 11:02:26 rherveille +// Fixed a bug where the core would signal an arbitration lost (AL bit set), when another master controls the bus and the other master generates a STOP bit. +// +// Revision 1.10 2003/08/09 07:01:33 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.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) + + // state machine variable + reg [16:0] c_state; // synopsys enum_state + + // + // 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) + begin + cnt <= #1 clk_cnt; + clk_en <= #1 1'b1; + end + else if (slave_wait) + 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) | (|c_state & 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; + + 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) // synopsys full_case parallel_case + // idle state + idle: + begin + case (cmd) // synopsys 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 diff --git a/i2c_slave_opencores/model/i2c_master_byte_ctrl.v b/i2c_slave_opencores/model/i2c_master_byte_ctrl.v new file mode 100644 index 0000000..0cb1a33 --- /dev/null +++ b/i2c_slave_opencores/model/i2c_master_byte_ctrl.v @@ -0,0 +1,344 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// 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.1 2008-11-08 13:15:10 sfielding Exp $ +// +// $Date: 2008-11-08 13:15:10 $ +// $Revision: 1.1 $ +// $Author: sfielding $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: not supported by cvs2svn $ +// Revision 1.7 2004/02/18 11:40:46 rherveille +// Fixed a potential bug in the statemachine. During a 'stop' 2 cmd_ack signals were generated. Possibly canceling a new start command. +// +// Revision 1.6 2003/08/09 07:01:33 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.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) // synopsys 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; + 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; + + 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 diff --git a/i2c_slave_opencores/model/i2c_master_defines.v b/i2c_slave_opencores/model/i2c_master_defines.v new file mode 100644 index 0000000..ff0efa3 --- /dev/null +++ b/i2c_slave_opencores/model/i2c_master_defines.v @@ -0,0 +1,64 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// 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.1 2008-11-08 13:15:10 sfielding Exp $ +// +// $Date: 2008-11-08 13:15:10 $ +// $Revision: 1.1 $ +// $Author: sfielding $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: not supported by cvs2svn $ +// 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. +// + + +// 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 diff --git a/i2c_slave_opencores/model/i2c_master_top.v b/i2c_slave_opencores/model/i2c_master_top.v new file mode 100644 index 0000000..aa823cb --- /dev/null +++ b/i2c_slave_opencores/model/i2c_master_top.v @@ -0,0 +1,301 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// 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.1 2008-11-08 13:15:10 sfielding Exp $ +// +// $Date: 2008-11-08 13:15:10 $ +// $Revision: 1.1 $ +// $Author: sfielding $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: not supported by cvs2svn $ +// Revision 1.11 2005/02/27 09:26:24 rherveille +// Fixed register overwrite issue. +// Removed full_case pragma, replaced it by a default statement. +// +// Revision 1.10 2003/09/01 10:34:38 rherveille +// Fix a blocking vs. non-blocking error in the wb_dat output mux. +// +// 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 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 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; + default: ; + 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 diff --git a/i2c_slave_opencores/model/wb_master_model.v b/i2c_slave_opencores/model/wb_master_model.v new file mode 100644 index 0000000..4f32542 --- /dev/null +++ b/i2c_slave_opencores/model/wb_master_model.v @@ -0,0 +1,176 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// wb_master_model.v //// +//// //// +//// This file is part of the SPI IP core project //// +//// http://www.opencores.org/projects/spi/ //// +//// //// +//// Author(s): //// +//// - Simon Srot (simons@opencores.org) //// +//// //// +//// Based on: //// +//// - i2c/bench/verilog/wb_master_model.v //// +//// Copyright (C) 2001 Richard Herveille //// +//// //// +//// All additional information is avaliable in the Readme.txt //// +//// file. //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2002 Authors //// +//// //// +//// 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 source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// + +`include "timescale.v" + +module wb_master_model(clk, rst, adr, din, dout, cyc, stb, we, sel, ack, err, rty); + + parameter dwidth = 32; + parameter awidth = 32; + + input clk, rst; + output [awidth -1:0] adr; + input [dwidth -1:0] din; + output [dwidth -1:0] dout; + output cyc, stb; + output we; + output [dwidth/8 -1:0] sel; + input ack, err, rty; + + // Internal signals + reg [awidth -1:0] adr; + reg [dwidth -1:0] dout; + reg cyc, stb; + reg we; + reg [dwidth/8 -1:0] sel; + + reg [dwidth -1:0] q; + + // Memory Logic + initial + begin + adr = {awidth{1'bx}}; + dout = {dwidth{1'bx}}; + cyc = 1'b0; + stb = 1'bx; + we = 1'hx; + sel = {dwidth/8{1'bx}}; + #1; + end + + // Wishbone write cycle + task wb_write; + input delay; + integer delay; + + input [awidth -1:0] a; + input [dwidth -1:0] d; + + begin + + // wait initial delay + repeat(delay) @(posedge clk); + + // assert wishbone signal + #1; + adr = a; + dout = d; + cyc = 1'b1; + stb = 1'b1; + we = 1'b1; + sel = {dwidth/8{1'b1}}; + @(posedge clk); + + // wait for acknowledge from slave + while(~ack) @(posedge clk); + + // negate wishbone signals + #1; + cyc = 1'b0; + stb = 1'bx; + adr = {awidth{1'bx}}; + dout = {dwidth{1'bx}}; + we = 1'hx; + sel = {dwidth/8{1'bx}}; + + end + endtask + + // Wishbone read cycle + task wb_read; + input delay; + integer delay; + + input [awidth -1:0] a; + output [dwidth -1:0] d; + + begin + + // wait initial delay + repeat(delay) @(posedge clk); + + // assert wishbone signals + #1; + adr = a; + dout = {dwidth{1'bx}}; + cyc = 1'b1; + stb = 1'b1; + we = 1'b0; + sel = {dwidth/8{1'b1}}; + @(posedge clk); + + // wait for acknowledge from slave + while(~ack) @(posedge clk); + + // negate wishbone signals + #1; + cyc = 1'b0; + stb = 1'bx; + adr = {awidth{1'bx}}; + dout = {dwidth{1'bx}}; + we = 1'hx; + sel = {dwidth/8{1'bx}}; + d = din; + + end + endtask + + // Wishbone compare cycle (read data from location and compare with expected data) + task wb_cmp; + input delay; + integer delay; + + input [awidth -1:0] a; + input [dwidth -1:0] d_exp; + + begin + wb_read (delay, a, q); + + if (d_exp !== q) + $display("Data compare error. Received %h, expected %h at time %t", q, d_exp, $time); + end + endtask + +endmodule + diff --git a/i2c_slave_opencores/rtl/i2cSlave.v b/i2c_slave_opencores/rtl/i2cSlave.v new file mode 100644 index 0000000..a5fff46 --- /dev/null +++ b/i2c_slave_opencores/rtl/i2cSlave.v @@ -0,0 +1,199 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// i2cSlave.v //// +//// //// +//// This file is part of the i2cSlave opencores effort. +//// //// +//// //// +//// Module Description: //// +//// You will need to modify this file to implement your +//// interface. +//// //// +//// To Do: //// +//// +//// //// +//// Author(s): //// +//// - Steve Fielding, sfielding@base2designs.com //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2008 Steve Fielding and OPENCORES.ORG //// +//// //// +//// 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 source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from //// +//// //// +////////////////////////////////////////////////////////////////////// +// +`include "i2cSlave_define.v" + + +module i2cSlave ( + clk, + rst, + sda, + scl, + tb_readEn, + tb_writeEn, + tb_addr, + tb_dataIn, + tb_dataOut +); + +input clk; +input rst; +inout sda; +input scl; + +// tb interface +input tb_readEn; +input tb_writeEn; +input [15:0] tb_addr; +input [15:0] tb_dataIn; +output [15:0] tb_dataOut; + +// local wires and regs +reg sdaDeb; +reg sclDeb; +reg [`DEB_I2C_LEN-1:0] sdaPipe; +reg [`DEB_I2C_LEN-1:0] sclPipe; + +reg [`SCL_DEL_LEN-1:0] sclDelayed; +reg [`SDA_DEL_LEN-1:0] sdaDelayed; +reg [1:0] startStopDetState; +wire clearStartStopDet; +wire sdaOut; +wire sdaIn; +wire [15:0] regAddr; +wire [7:0] dataToRegIF; +wire writeEn; +wire [7:0] dataFromRegIF; +reg [1:0] rstPipe; +wire rstSyncToClk; +reg startEdgeDet; + +assign sda = (sdaOut == 1'b0) ? 1'b0 : 1'bz; +assign sdaIn = sda; + +// sync rst rsing edge to clk +always @(posedge clk) begin + if (rst == 1'b1) + rstPipe <= 2'b11; + else + rstPipe <= {rstPipe[0], 1'b0}; +end + +assign rstSyncToClk = rstPipe[1]; + +// debounce sda and scl +always @(posedge clk) begin + if (rstSyncToClk == 1'b1) begin + sdaPipe <= {`DEB_I2C_LEN{1'b1}}; + sdaDeb <= 1'b1; + sclPipe <= {`DEB_I2C_LEN{1'b1}}; + sclDeb <= 1'b1; + end + else begin + sdaPipe <= {sdaPipe[`DEB_I2C_LEN-2:0], sdaIn}; + sclPipe <= {sclPipe[`DEB_I2C_LEN-2:0], scl}; + if (&sclPipe[`DEB_I2C_LEN-1:1] == 1'b1) + sclDeb <= 1'b1; + else if (|sclPipe[`DEB_I2C_LEN-1:1] == 1'b0) + sclDeb <= 1'b0; + if (&sdaPipe[`DEB_I2C_LEN-1:1] == 1'b1) + sdaDeb <= 1'b1; + else if (|sdaPipe[`DEB_I2C_LEN-1:1] == 1'b0) + sdaDeb <= 1'b0; + end +end + + +// delay scl and sda +// sclDelayed is used as a delayed sampling clock +// sdaDelayed is only used for start stop detection +// Because sda hold time from scl falling is 0nS +// sda must be delayed with respect to scl to avoid incorrect +// detection of start/stop at scl falling edge. +always @(posedge clk) begin + if (rstSyncToClk == 1'b1) begin + sclDelayed <= {`SCL_DEL_LEN{1'b1}}; + sdaDelayed <= {`SDA_DEL_LEN{1'b1}}; + end + else begin + sclDelayed <= {sclDelayed[`SCL_DEL_LEN-2:0], sclDeb}; + sdaDelayed <= {sdaDelayed[`SDA_DEL_LEN-2:0], sdaDeb}; + end +end + +// start stop detection +always @(posedge clk) begin + if (rstSyncToClk == 1'b1) begin + startStopDetState <= `NULL_DET; + startEdgeDet <= 1'b0; + end + else begin + if (sclDeb == 1'b1 && sdaDelayed[`SDA_DEL_LEN-2] == 1'b0 && sdaDelayed[`SDA_DEL_LEN-1] == 1'b1) + startEdgeDet <= 1'b1; + else + startEdgeDet <= 1'b0; + if (clearStartStopDet == 1'b1) + startStopDetState <= `NULL_DET; + else if (sclDeb == 1'b1) begin + if (sdaDelayed[`SDA_DEL_LEN-2] == 1'b1 && sdaDelayed[`SDA_DEL_LEN-1] == 1'b0) + startStopDetState <= `STOP_DET; + else if (sdaDelayed[`SDA_DEL_LEN-2] == 1'b0 && sdaDelayed[`SDA_DEL_LEN-1] == 1'b1) + startStopDetState <= `START_DET; + end + end +end + + +registerInterface u_registerInterface( + .clk(clk), + .addr(regAddr), + .dataIn(dataToRegIF), + .writeEn(writeEn), + .dataOut(dataFromRegIF), + .tb_readEn(tb_readEn), + .tb_writeEn(tb_writeEn), + .tb_addr(tb_addr), + .tb_dataIn(tb_dataIn), + .tb_dataOut(tb_dataOut) +); + +serialInterface u_serialInterface ( + .clk(clk), + .rst(rstSyncToClk | startEdgeDet), + .dataIn(dataFromRegIF), + .dataOut(dataToRegIF), + .writeEn(writeEn), + .regAddr(regAddr), + .scl(sclDelayed[`SCL_DEL_LEN-1]), + .sdaIn(sdaDeb), + .sdaOut(sdaOut), + .startStopDetState(startStopDetState), + .clearStartStopDet(clearStartStopDet) +); + + +endmodule + + + diff --git a/i2c_slave_opencores/rtl/i2cSlave_define.v b/i2c_slave_opencores/rtl/i2cSlave_define.v new file mode 100644 index 0000000..937ffcc --- /dev/null +++ b/i2c_slave_opencores/rtl/i2cSlave_define.v @@ -0,0 +1,68 @@ +// ----------------------- i2cSlave_define.v -------------------- + +// stream states +`define STREAM_IDLE 2'b00 +`define STREAM_READ 2'b01 +`define STREAM_WRITE_ADDR 2'b10 +`define STREAM_WRITE_DATA 2'b11 + +// start stop detection states +`define NULL_DET 2'b00 +`define START_DET 2'b01 +`define STOP_DET 2'b10 + +// i2c ack and nak +`define I2C_NAK 1'b1 +`define I2C_ACK 1'b0 + +// ---------------------------------------------------------------- +// ------------- modify constants below this line ----------------- +// ---------------------------------------------------------------- + +// i2c device address +`define I2C_ADDRESS 7'h3c + +// System clock frequency in MHz +// If you are using a clock frequency below 24MHz, then the macro +// for SDA_DEL_LEN will result in compile errors for i2cSlave.v +// you will need to hand tweak the SDA_DEL_LEN constant definition +`define CLK_FREQ 48 + +// Debounce SCL and SDA over this many clock ticks +// The rise time of SCL and SDA can be up to 1000nS (in standard mode) +// so it is essential to debounce the inputs. +// The spec requires 0.05V of hysteresis, but in practise +// simply debouncing the inputs is sufficient +// I2C spec requires suppresion of spikes of +// maximum duration 50nS, so this debounce time should be greater than 50nS +// Also increases data hold time and decreases data setup time +// during an I2C read operation +// 10 ticks = 208nS @ 48MHz +`define DEB_I2C_LEN (10*`CLK_FREQ)/48 + +// Delay SCL for use as internal sampling clock +// Using delayed version of SCL to ensure that +// SDA is stable when it is sampled. +// Not entirely citical, as according to I2C spec +// SDA should have a minimum of 100nS of set up time +// with respect to SCL rising edge. But with the very slow edge +// speeds used in I2C it is better to err on the side of caution. +// This delay also has the effect of adding extra hold time to the data +// with respect to SCL falling edge. I2C spec requires 0nS of data hold time. +// 10 ticks = 208nS @ 48MHz +`define SCL_DEL_LEN (10*`CLK_FREQ)/48 + +// Delay SDA for use in start/stop detection +// Use delayed SDA during start/stop detection to avoid +// incorrect detection at SCL falling edge. +// From I2C spec start/stop setup is 600nS with respect to SCL rising edge +// and start/stop hold is 600nS wrt SCL falling edge. +// So it is relatively easy to discriminate start/stop, +// but data setup time is a minimum of 100nS with respect to SCL rising edge +// and 0nS hold wrt to SCL falling edge. +// So the tricky part is providing robust start/stop detection +// in the presence of regular data transitions. +// This delay time should be less than 100nS +// 4 ticks = 83nS @ 48MHz +`define SDA_DEL_LEN (4*`CLK_FREQ)/48 + diff --git a/i2c_slave_opencores/rtl/registerInterface.v b/i2c_slave_opencores/rtl/registerInterface.v new file mode 100644 index 0000000..5bfe19f --- /dev/null +++ b/i2c_slave_opencores/rtl/registerInterface.v @@ -0,0 +1,109 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// registerInterface.v //// +//// //// +//// This file is part of the i2cSlave opencores effort. +//// //// +//// //// +//// Module Description: //// +//// You will need to modify this file to implement your +//// interface. +//// Add your control and status bytes/bits to module inputs and outputs, +//// and also to the I2C read and write process blocks +//// //// +//// To Do: //// +//// +//// //// +//// Author(s): //// +//// - Steve Fielding, sfielding@base2designs.com //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2008 Steve Fielding and OPENCORES.ORG //// +//// //// +//// 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 source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from //// +//// //// +////////////////////////////////////////////////////////////////////// +// +`include "i2cSlave_define.v" + + +module registerInterface ( + clk, + addr, + dataIn, + writeEn, + dataOut, + tb_readEn, + tb_writeEn, + tb_addr, + tb_dataIn, + tb_dataOut +); + +// i2c interface +input clk; +input [15:0] addr; +input [7:0] dataIn; +input writeEn; +output [7:0] dataOut; +reg [7:0] dataOut; + +// speicher +reg [7:0] memory [16'hffff:0]; + +// tb interface +input tb_readEn; +input tb_writeEn; +input [15:0] tb_addr; +input [15:0] tb_dataIn; +output [15:0] tb_dataOut; +reg [15:0] tb_dataOut; + +// --- TB Read +always @(posedge tb_readEn) begin + tb_dataOut[15:8] <= memory[tb_addr]; + tb_dataOut[7:0] <= memory[tb_addr + 1'b1]; +end + +// --- TB Write +always @(posedge tb_writeEn) begin + memory[tb_addr] <= tb_dataIn[15:8]; + memory[tb_addr + 1'b1] <= tb_dataIn[7:0]; +end + +// --- I2C Read +always @(posedge clk) begin + dataOut <= memory[addr]; +end + +// --- I2C Write +always @(posedge clk) begin + if (writeEn == 1'b1) begin + memory[addr] <= dataIn; + end +end + +endmodule + + + diff --git a/i2c_slave_opencores/rtl/serialInterface.v b/i2c_slave_opencores/rtl/serialInterface.v new file mode 100644 index 0000000..4f79092 --- /dev/null +++ b/i2c_slave_opencores/rtl/serialInterface.v @@ -0,0 +1,372 @@ + +////////////////////////////////////////////////////////////////////// +//// //// +//// serialInterface.v //// +//// //// +//// This file is part of the i2cSlave opencores effort. +//// //// +//// //// +//// Module Description: //// +//// Perform all serial to parallel, and parallel +//// to serial conversions. Perform device address matching +//// Handle arbitrary length I2C reads terminated by NAK +//// from host, and arbitrary length I2C writes terminated +//// by STOP from host +//// The second byte of a I2C write is always interpreted +//// as a register address, and becomes the base register address +//// for all read and write transactions. +//// I2C WRITE: devAddr, regAddr, data[regAddr], data[regAddr+1], ..... data[regAddr+N] +//// I2C READ: data[regAddr], data[regAddr+1], ..... data[regAddr+N] +//// Note that when regAddR reaches 255 it will automatically wrap round to 0 +//// //// +//// To Do: //// +//// +//// //// +//// Author(s): //// +//// - Steve Fielding, sfielding@base2designs.com //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2008 Steve Fielding and OPENCORES.ORG //// +//// //// +//// 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 source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source is distributed in the hope that it will be //// +//// useful, but WITHOUT ANY WARRANTY; without even the implied //// +//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// +//// PURPOSE. See the GNU Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from //// +//// //// +////////////////////////////////////////////////////////////////////// +// +`include "timescale.v" +`include "i2cSlave_define.v" + +module serialInterface (clearStartStopDet, clk, dataIn, dataOut, regAddr, rst, scl, sdaIn, sdaOut, startStopDetState, writeEn); +input clk; +input [7:0]dataIn; +input rst; +input scl; +input sdaIn; +input [1:0]startStopDetState; +output clearStartStopDet; +output [7:0]dataOut; +output [15:0]regAddr; +output sdaOut; +output writeEn; + +reg clearStartStopDet, next_clearStartStopDet; +wire clk; +wire [7:0]dataIn; +reg [7:0]dataOut, next_dataOut; +reg [15:0]regAddr, next_regAddr; +reg regAddr_hiByte = 1'b0; //high and low byte of regAddr +wire rst; +wire scl; +wire sdaIn; +reg sdaOut, next_sdaOut; +wire [1:0]startStopDetState; +reg writeEn, next_writeEn; + +// diagram signals declarations +reg [2:0]bitCnt, next_bitCnt; +reg [7:0]rxData, next_rxData; +reg [1:0]streamSt, next_streamSt; +reg [7:0]txData, next_txData; + +// BINARY ENCODED state machine: SISt +// State codes definitions: +`define START 4'b0000 +`define CHK_RD_WR 4'b0001 +`define READ_RD_LOOP 4'b0010 +`define READ_WT_HI 4'b0011 +`define READ_CHK_LOOP_FIN 4'b0100 +`define READ_WT_LO 4'b0101 +`define READ_WT_ACK 4'b0110 +`define WRITE_WT_LO 4'b0111 +`define WRITE_WT_HI 4'b1000 +`define WRITE_CHK_LOOP_FIN 4'b1001 +`define WRITE_LOOP_WT_LO 4'b1010 +`define WRITE_ST_LOOP 4'b1011 +`define WRITE_WT_LO2 4'b1100 +`define WRITE_WT_HI2 4'b1101 +`define WRITE_CLR_WR 4'b1110 +`define WRITE_CLR_ST_STOP 4'b1111 + +reg [3:0]CurrState_SISt, NextState_SISt; + +// Diagram actions (continuous assignments allowed only: assign ...) +// diagram ACTION + + +// Machine: SISt + +// NextState logic (combinatorial) +always @ (startStopDetState or streamSt or scl or txData or bitCnt or rxData or sdaIn or regAddr or dataIn or sdaOut or writeEn or dataOut or clearStartStopDet or CurrState_SISt) +begin + NextState_SISt <= CurrState_SISt; + // Set default values for outputs and signals + next_streamSt <= streamSt; + next_txData <= txData; + next_rxData <= rxData; + next_sdaOut <= sdaOut; + next_writeEn <= writeEn; + next_dataOut <= dataOut; + next_bitCnt <= bitCnt; + next_clearStartStopDet <= clearStartStopDet; + next_regAddr <= regAddr; + case (CurrState_SISt) // synopsys parallel_case full_case + `START: + begin + next_streamSt <= `STREAM_IDLE; + next_txData <= 8'h00; + next_rxData <= 8'h00; + next_sdaOut <= 1'b1; + next_writeEn <= 1'b0; + next_dataOut <= 8'h00; + next_bitCnt <= 3'b000; + next_clearStartStopDet <= 1'b0; + NextState_SISt <= `CHK_RD_WR; + end + `CHK_RD_WR: + begin + if (streamSt == `STREAM_READ) + begin + NextState_SISt <= `READ_RD_LOOP; + next_txData <= dataIn; + next_regAddr <= regAddr + 1'b1; + next_bitCnt <= 3'b001; + end + else + begin + NextState_SISt <= `WRITE_WT_HI; + next_rxData <= 8'h00; + end + end + `READ_RD_LOOP: + begin + if (scl == 1'b0) + begin + NextState_SISt <= `READ_WT_HI; + next_sdaOut <= txData [7]; + next_txData <= {txData [6:0], 1'b0}; + end + end + `READ_WT_HI: + begin + if (scl == 1'b1) + begin + NextState_SISt <= `READ_CHK_LOOP_FIN; + end + end + `READ_CHK_LOOP_FIN: + begin + if (bitCnt == 3'b000) + begin + NextState_SISt <= `READ_WT_LO; + end + else + begin + NextState_SISt <= `READ_RD_LOOP; + next_bitCnt <= bitCnt + 1'b1; + end + end + `READ_WT_LO: + begin + if (scl == 1'b0) + begin + NextState_SISt <= `READ_WT_ACK; + next_sdaOut <= 1'b1; + end + end + `READ_WT_ACK: + begin + if (scl == 1'b1) + begin + NextState_SISt <= `CHK_RD_WR; + if (sdaIn == `I2C_NAK) + next_streamSt <= `STREAM_IDLE; + end + end + `WRITE_WT_LO: + begin + if ((scl == 1'b0) && (startStopDetState == `STOP_DET || + (streamSt == `STREAM_IDLE && startStopDetState == `NULL_DET))) + begin + NextState_SISt <= `WRITE_CLR_ST_STOP; + case (startStopDetState) + `NULL_DET: + next_bitCnt <= bitCnt + 1'b1; + `START_DET: begin + next_streamSt <= `STREAM_IDLE; + next_rxData <= 8'h00; + end + default: ; + endcase + next_streamSt <= `STREAM_IDLE; + next_clearStartStopDet <= 1'b1; + end + else if (scl == 1'b0) + begin + NextState_SISt <= `WRITE_ST_LOOP; + case (startStopDetState) + `NULL_DET: + next_bitCnt <= bitCnt + 1'b1; + `START_DET: begin + next_streamSt <= `STREAM_IDLE; + next_rxData <= 8'h00; + end + default: ; + endcase + end + end + `WRITE_WT_HI: + begin + if (scl == 1'b1) + begin + NextState_SISt <= `WRITE_WT_LO; + next_rxData <= {rxData [6:0], sdaIn}; + next_bitCnt <= 3'b000; + end + end + `WRITE_CHK_LOOP_FIN: + begin + if (bitCnt == 3'b111) + begin + NextState_SISt <= `WRITE_CLR_WR; + next_sdaOut <= `I2C_ACK; + case (streamSt) + `STREAM_IDLE: begin + if (rxData[7:1] == `I2C_ADDRESS && startStopDetState == `START_DET) begin + if (rxData[0] == 1'b1) + next_streamSt <= `STREAM_READ; + else + next_streamSt <= `STREAM_WRITE_ADDR; + end + else + next_sdaOut <= `I2C_NAK; + end + `STREAM_WRITE_ADDR: begin + if(regAddr_hiByte == 0) begin + next_regAddr[15:8] <= rxData; + regAddr_hiByte <= 1; + end + else begin + next_streamSt <= `STREAM_WRITE_DATA; + next_regAddr[7:0] <= rxData; + regAddr_hiByte <= 0; + end + end + `STREAM_WRITE_DATA: begin + next_dataOut <= rxData; + next_writeEn <= 1'b1; + end + default: + next_streamSt <= streamSt; + endcase + end + else + begin + NextState_SISt <= `WRITE_ST_LOOP; + next_bitCnt <= bitCnt + 1'b1; + end + end + `WRITE_LOOP_WT_LO: + begin + if (scl == 1'b0) + begin + NextState_SISt <= `WRITE_CHK_LOOP_FIN; + end + end + `WRITE_ST_LOOP: + begin + if (scl == 1'b1) + begin + NextState_SISt <= `WRITE_LOOP_WT_LO; + next_rxData <= {rxData [6:0], sdaIn}; + end + end + `WRITE_WT_LO2: + begin + if (scl == 1'b0) + begin + NextState_SISt <= `CHK_RD_WR; + next_sdaOut <= 1'b1; + end + end + `WRITE_WT_HI2: + begin + next_clearStartStopDet <= 1'b0; + if (scl == 1'b1) + begin + NextState_SISt <= `WRITE_WT_LO2; + end + end + `WRITE_CLR_WR: + begin + if (writeEn == 1'b1) + next_regAddr <= regAddr + 1'b1; + next_writeEn <= 1'b0; + next_clearStartStopDet <= 1'b1; + NextState_SISt <= `WRITE_WT_HI2; + end + `WRITE_CLR_ST_STOP: + begin + next_clearStartStopDet <= 1'b0; + NextState_SISt <= `CHK_RD_WR; + end + endcase +end + +// Current State Logic (sequential) +always @ (posedge clk) +begin + if (rst == 1'b1) + CurrState_SISt <= `START; + else + CurrState_SISt <= NextState_SISt; +end + +// Registered outputs logic +always @ (posedge clk) +begin + if (rst == 1'b1) + begin + sdaOut <= 1'b1; + writeEn <= 1'b0; + dataOut <= 8'h00; + clearStartStopDet <= 1'b0; + // regAddr <= // Initialization in the reset state or default value required!! + streamSt <= `STREAM_IDLE; + txData <= 8'h00; + rxData <= 8'h00; + bitCnt <= 3'b000; + end + else + begin + sdaOut <= next_sdaOut; + writeEn <= next_writeEn; + dataOut <= next_dataOut; + clearStartStopDet <= next_clearStartStopDet; + regAddr <= next_regAddr; + streamSt <= next_streamSt; + txData <= next_txData; + rxData <= next_rxData; + bitCnt <= next_bitCnt; + end +end + +endmodule \ No newline at end of file diff --git a/i2c_slave_opencores/rtl/timescale.v b/i2c_slave_opencores/rtl/timescale.v new file mode 100644 index 0000000..edd8d7a --- /dev/null +++ b/i2c_slave_opencores/rtl/timescale.v @@ -0,0 +1,5 @@ +////////////////////////////////////////////////////////////////////// +// timescale.v +////////////////////////////////////////////////////////////////////// +`timescale 1ns / 1ps + diff --git a/i2c_slave_opencores/sim/build_icarus.bat b/i2c_slave_opencores/sim/build_icarus.bat new file mode 100644 index 0000000..dd87e77 --- /dev/null +++ b/i2c_slave_opencores/sim/build_icarus.bat @@ -0,0 +1,2 @@ +#!/bin/sh +iverilog -o testHarness -c filelist.icarus diff --git a/i2c_slave_opencores/sim/filelist.icarus b/i2c_slave_opencores/sim/filelist.icarus new file mode 100644 index 0000000..7d33b1b --- /dev/null +++ b/i2c_slave_opencores/sim/filelist.icarus @@ -0,0 +1,16 @@ +../rtl/serialInterface.v +../rtl/registerInterface.v +../rtl/i2cSlave.v +../model/i2c_master_bit_ctrl.v +../model/i2c_master_byte_ctrl.v +../model/i2c_master_top.v +../model/wb_master_model.v +../bench/multiByteReadWrite.v +../bench/testHarness.v +../bench/testCase0.v + ++incdir+../rtl ++incdir+../bench ++incdir+../model ++define+SIM_COMPILE + diff --git a/i2c_slave_opencores/sim/gtkwave.ini b/i2c_slave_opencores/sim/gtkwave.ini new file mode 100644 index 0000000..bb5f6d7 --- /dev/null +++ b/i2c_slave_opencores/sim/gtkwave.ini @@ -0,0 +1,50 @@ +# +# sample rc file +# +hier_max_level 1 +force_toolbars 0 + +dynamic_resizing 1 +hpane_pack 1 +use_vcd 0 +#initial_window_x 700 +#initial_window_y 400 +use_maxtime_display 0 + +enable_vcd_autosave 0 +use_roundcaps 1 + +use_nonprop_fonts yes +enable_horiz_grid yes +use_big_fonts no +constant_marker_update yes +show_grid yes +show_base_symbols no +use_roundcaps yes + +atomic_vectors yes +vcd_explicit_zero_subscripts no + +# +# color additions +# +color_back 000000 +color_grid 202070 +color_high 00ff00 +color_low 008000 +color_trans 00c000 +color_mid c0c000 + +color_value ffffff +color_vbox 00ff00 +color_vtrans 00c000 + +color_x 00ff00 +color_xfill 004000 + +color_umark ff8080 +color_mark ffff80 + +color_time ffffff +color_timeb 000000 + diff --git a/i2c_slave_opencores/sim/myWave.sav b/i2c_slave_opencores/sim/myWave.sav new file mode 100644 index 0000000..f155467 --- /dev/null +++ b/i2c_slave_opencores/sim/myWave.sav @@ -0,0 +1,10 @@ +*-27.236394 43400000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +@28 +testHarness.u_i2cSlave.scl +testHarness.u_i2cSlave.sda +@22 +testHarness.u_i2cSlave.tb_readEn +testHarness.u_i2cSlave.tb_writeEn +testHarness.u_i2cSlave.tb_addr[15:0] +testHarness.u_i2cSlave.tb_dataIn[15:0] +testHarness.u_i2cSlave.tb_dataOut[15:0] diff --git a/i2c_slave_opencores/sim/run_icarus.bat b/i2c_slave_opencores/sim/run_icarus.bat new file mode 100644 index 0000000..9d6aae0 --- /dev/null +++ b/i2c_slave_opencores/sim/run_icarus.bat @@ -0,0 +1,2 @@ +vvp testHarness + diff --git a/i2c_slave_opencores/sim/simulate.sh b/i2c_slave_opencores/sim/simulate.sh new file mode 100644 index 0000000..9b0aab4 --- /dev/null +++ b/i2c_slave_opencores/sim/simulate.sh @@ -0,0 +1,2 @@ +#!/bin/sh +iverilog -o testHarness -cfilelist.icarus && vvp testHarness diff --git a/i2c_slave_opencores/sim/viewWave.bat b/i2c_slave_opencores/sim/viewWave.bat new file mode 100644 index 0000000..45f2687 --- /dev/null +++ b/i2c_slave_opencores/sim/viewWave.bat @@ -0,0 +1 @@ +gtkwave wave.vcd myWave.sav