📄 i2c_slave.v
字号:
//----------------------------------------------------------------------------
//
// Name: i2c_slave.v
//
// Description: I2C slave simulation model for I2C serial controller
//
// $Revision: 1.0 $
//
// Copyright 2004 Lattice Semiconductor Corporation. All rights reserved.
//
//----------------------------------------------------------------------------
// Permission:
//
// Lattice Semiconductor grants permission to use this code for use
// in synthesis for any Lattice programmable logic product. Other
// use of this code, including the selling or duplication of any
// portion is strictly prohibited.
//
// Disclaimer:
//
// This VHDL or Verilog source code is intended as a design reference
// which illustrates how these types of functions can be implemented.
// It is the user's responsibility to verify their design for
// consistency and functionality through the use of formal
// verification methods. Lattice Semiconductor provides no warranty
// regarding the use or functionality of this code.
//----------------------------------------------------------------------------
//
// Lattice Semiconductor Corporation
// 5555 NE Moore Court
// Hillsboro, OR 97124
// U.S.A
//
// TEL: 1-800-Lattice (USA and Canada)
// 408-826-6000 (other locations)
//
// web: http://www.latticesemi.com/
// email: techsupport@latticesemi.com
//
//----------------------------------------------------------------------------
`timescale 1 ns / 1 ps
/*
This is a generic standard mode slave model for I2C.
*/
module i2c_slave ( scl,
sda);
//-------------------------------------------------------------------
// port list
input scl;
inout sda;
//-------------------------------------------------------------------
// wire declarations
wire scl;
wire sda;
//-------------------------------------------------------------------
// reg declarations
reg clk, rst_l;
reg [7:0] mem [255:0]; // 2^8 = 256 locations
reg [7:0] word_address; // counts the active byte
reg start_detect;
reg stop_detect;
reg sda_reg, sda_reg_delayed;
reg scl_reg, scl_reg_delayed;
reg start_pulse, stop_pulse, scl_pulse, scl_neg_pulse;
reg [6:0] address_reg_7; // All 7 Bits of 7 bit addressing
reg [9:0] temp10;
reg [6:0] address_reg_10_upper; // Upper 2 bits of address
reg [7:0] address_reg_10_lower; // lower 8 bits of address
reg [3:0] current_state, next_state;
reg [7:0] in_reg, out_reg; // registers used to hold the input
// and output data to-from the sda line
reg [3:0] bit_counter; // Used to counter what bit is being selected
// for the in_reg and out_reg
reg r_w_bit; // used to hold the read write bit;
reg hit_7, hit_10_upper, hit_10_lower; // flags for address hits
reg sda_out;
reg in_reg_enable; // the clock enable for the in_reg registers.
reg out_en; // the output enable
reg word_add_flag, ack_flag;
reg [7:1] temp_add_upper;
reg [7:0] temp_add_lower; // temp_add_upper & temp_add_lower are
// used to hold the first &
// second address bytes of 10 bit
// addressing so that during a 10 bit addressing
// read the value of the current 10 bit address
// can be compared with the last read.
reg read_10_flag; // This flag is set when the temp_add matches the current
// address_reg_10_upper and the r/w is a 1. This tells
// the ack to goto a data read state instead of getting
// the second byte of address.
//-------------------------------------------------------------------
// misc variables
integer i;
//-------------------------------------------------------------------
// defines
// used for address_mode parameter
`define seven_bit 0
// used for address_mode parameter
`define ten_bit 1
// used in upper 5 bits of address_reg_10_upper
// DON'T CHANGE
`define ten_bit_add 5'b11110
// a 1 turns this on and a 0 off
`define debug 0
//-------------------------------------------------------------------
// parameters
parameter period = 30; // using 33 MHz
parameter reset_time = 20; // hold reset low this long
// DESIGNER SET the following parameter to use 7 or 10 bit addressing
parameter address_mode = `seven_bit; // Use `seven_bit or `ten_bit
// depending on the value in address_mode either seven_bit_address or
// ten_bit_address will be used.
// DESIGNER SET the next parameter with the 7 bit address the slave
// should respond to. MSB->LSB
// example: 7'b1010_000;
parameter seven_bit_address = 7'b1010_000;
// DESIGNER SET the next parameter with the 10 bit address the slave
// should respond to. MSB->LSB
// example: 10'b10_1100_1010;
parameter ten_bit_address = 10'b10_1100_1010;
// state bits
parameter [4:0] idle = 0,
start = 1,
address = 2,
ack = 3,
data = 4,
data_ack = 5;
parameter tdh = 100; // tdh = data out hold time min
//-------------------------------------------------------------------
// internal clock for the model
always #(period/2) clk = ~clk;
//------------------------------------------------------------------
// print some status
always @(posedge scl)
if (`debug) begin
$display($time,"%m: Received Clock Data = %b",sda);
end
//-------------------------------------------------------------------
// initialize the address registers, mem array, clk and control the reset
initial
begin
// initialize the address registers
if (address_mode == `seven_bit) begin
$display("%m Using 7 Bit Addressing");
address_reg_7 = seven_bit_address;
end
else if (address_mode == `ten_bit) begin
$display("%m Using 10 Bit Addressing");
temp10 = ten_bit_address;
address_reg_10_upper = {`ten_bit_add, temp10[9:8]}; // 2 MSB
address_reg_10_lower = temp10[7:0];
end
else begin
$display( "/n ERROR: address_mode parameter is INVALID in %m !! /n");
end
// initialize the mem array
for (i = 0; i < 256; i = i + 1) begin
mem[i] = i;
end
r_w_bit = 1'b0;
out_reg = 8'b0;
out_en = 0; // disable output
bit_counter = 0;
word_address = 8'b0; // initialize byte #
word_add_flag = 0;
ack_flag = 0;
in_reg_enable = 0;
temp_add_upper = 0;
temp_add_lower = 0;
read_10_flag = 0;
hit_10_upper = 0;
hit_10_lower = 0;
hit_7 = 0;
clk = 1'b0; // initalize clock
next_state = 5'b0;
rst_l = 1'b0; // turn on reset signal
#reset_time;
rst_l = 1'b1; // turn off reset signal
end
//-------------------------------------------------------------------------
// sda_out is an internal reg that is assigned a 0 when the output should be
// 0 and it assigns a Z otherwise.
assign sda = (sda_out == 0 && out_en) ? 1'b0 : 1'bZ;
//--------------------------------------------------------------------
// start and stop detect logic
always @ (posedge clk or negedge rst_l)
begin
if (!rst_l) begin
sda_reg <= 1; // bus is active low
sda_reg_delayed <= 1;
end
else begin
sda_reg <= sda;
sda_reg_delayed <= sda_reg;
end
end
// detect a high to low while scl is high
// start_pulse
always @ (posedge clk or negedge rst_l)
begin
if (!rst_l) begin
start_pulse = 0;
end
else if (!sda_reg && sda_reg_delayed && scl) begin
start_pulse = 1;
end
else begin
start_pulse = 0;
end
end
// start flag
always @ (posedge clk or negedge rst_l)
begin
if (!rst_l) begin
start_detect <= 0;
end
else if (start_pulse) begin
start_detect <= 1;
end
else if (!scl) begin
start_detect <= 0; // clear start bit
end
else begin
start_detect <= start_detect;
end
end
// detect a low to high while scl is high
// stop_pulse
always @ (posedge clk or negedge rst_l)
begin
if (!rst_l) begin
stop_pulse = 0;
end
else if (sda_reg && !sda_reg_delayed && scl) begin
stop_pulse = 1;
end
else begin
stop_pulse = 0;
end
end
//stop flag
always @ (posedge clk or negedge rst_l)
begin
if (!rst_l) begin
stop_detect <= 0;
end
else if (stop_pulse) begin
stop_detect <= 1;
end
else if (current_state == idle) begin
stop_detect <= 0; // clear start bit
end
end
//--------------------------------------
//--------------------------------------
// SCL posedge & nededge detector regs
always @ (posedge clk or negedge rst_l)
begin
if (!rst_l) begin
scl_reg <= 1;
scl_reg_delayed <= 1;
end
else begin
scl_reg <= scl;
scl_reg_delayed <= scl_reg;
end
end
// SCL posedge detector
always @ (posedge clk or negedge rst_l)
begin
if (!rst_l) begin
scl_pulse <= 0;
end
else if (scl_reg && !scl_reg_delayed) begin
scl_pulse <= 1;
end
else begin
scl_pulse <= 0;
end
end
// SCL negedge detector
always @ (posedge clk or negedge rst_l)
begin
if (!rst_l) begin
scl_neg_pulse <= 0;
end
else if (!scl_reg && scl_reg_delayed) begin
scl_neg_pulse <= 1;
end
else begin
scl_neg_pulse <= 0;
end
end
//----------------------------------------
//----------------------------------------
// Output Mux
always @ (bit_counter or out_reg)
case (bit_counter)
4'd0: sda_out = out_reg[7];
4'd1: sda_out = out_reg[6];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -