📄 i2c_slave.v
字号:
//**********************************************************************
//
// File: i2c_slave.v
// Module:i2c_slave
// IME of CAS
// by jgchen
//
//
//**********************************************************************
`timescale 1ns/1ps
module i2c_slave(
reset_n,
clock,
sda_out,
sda_in,
scl,
sda_en,
data_reg0,
data_reg1
);
input clock;
input reset_n;
input sda_in;
input scl;
output [7:0] data_reg0; //to define eight register
output [7:0] data_reg1;
output sda_en;
reg sda_en;
output sda_out;
reg reset_n1;
reg reset_n2;
reg scl_regi0;
reg scl_regi;
reg sda_regi0;
reg sda_regi;
reg start_bus_reg;
reg stop_bus_reg;
reg [7:0] data_reg0;
reg [7:0] data_reg1;
reg [6:0] addr_in_reg;
reg [7:0] data_in_reg0;
reg [7:0] data_in_reg1;
reg [7:0] reg_addr;
reg [3:0] main_state;
reg [2:0] addr_in_state;
reg [3:0] data_in_state;
reg [3:0] data_out_state;
reg [3:0] reg_addr_state;
reg sda_out1; // ACK
reg sda_out2; // data_to_master
reg write_read;
reg [1:0] ack_state;
reg flag;
assign sda_out = flag ? sda_out2 : sda_out1;
// ----------------------------------------------------------------
// reset_n, scl, sda_in -> two stages registered
always@(posedge clock)
begin
reset_n1 <= reset_n;
reset_n2 <= reset_n1;
end
always@(posedge clock or negedge reset_n2)
begin
if(!reset_n2)
begin
scl_regi <= 1'b0;
sda_regi <= 1'b0;
scl_regi0 <= 1'b0;
sda_regi0 <= 1'b0;
end
else
begin
scl_regi0 <= scl_regi;
scl_regi <= scl;
sda_regi0 <= sda_regi;
sda_regi <= sda_in;
end
end
// ----------------------------------------------------------------
// to test start condition: scl=1, sda_in=100
always@(posedge clock or negedge reset_n2)
begin
if(!reset_n2)
start_bus_reg <= 1'b0;
else
begin
if({sda_regi0,sda_regi,sda_in}==3'b100 && {scl_regi0,scl_regi,scl}==3'b111)
start_bus_reg <= 1'b1;
else
start_bus_reg <= 1'b0;
end
end
// ----------------------------------------------------------------
// to test stop condition: scl=1, sda_in=011
always@(posedge clock or negedge reset_n2)
begin
if(!reset_n2)
stop_bus_reg <= 1'b0;
else
begin
if({sda_regi0,sda_regi,sda_in}==3'b011 && {scl_regi0,scl_regi,scl}==3'b111)
stop_bus_reg <= 1'b1;
else
stop_bus_reg <= 1'b0;
end
end
//----------------- addr in statemachine -------------------------------
parameter addr_in6 = 3'h0; // chip_id
parameter addr_in5 = 3'h1;
parameter addr_in4 = 3'h2;
parameter addr_in3 = 3'h3;
parameter addr_in2 = 3'h4;
parameter addr_in1 = 3'h5;
parameter addr_in0 = 3'h6;
parameter addr_end = 3'h7;
//----------------- reg addr in statemachine ----------------------------
parameter reg_addr7 =4'h0;
parameter reg_addr6 =4'h1;
parameter reg_addr5 =4'h2;
parameter reg_addr4 =4'h3;
parameter reg_addr3 =4'h4;
parameter reg_addr2 =4'h5;
parameter reg_addr1 =4'h6;
parameter reg_addr0 =4'h7;
parameter reg_addr_end =4'h8;
//----------------- data in statemachine -------------------------------
parameter data_in7 = 4'h0;
parameter data_in6 = 4'h1;
parameter data_in5 = 4'h2;
parameter data_in4 = 4'h3;
parameter data_in3 = 4'h4;
parameter data_in2 = 4'h5;
parameter data_in1 = 4'h6;
parameter data_in0 = 4'h7;
parameter data_end = 4'h8;
//----------------- data out statemachine -------------------------------
parameter data_out7 = 4'h0;
parameter data_out6 = 4'h1;
parameter data_out5 = 4'h2;
parameter data_out4 = 4'h3;
parameter data_out3 = 4'h4;
parameter data_out2 = 4'h5;
parameter data_out1 = 4'h6;
parameter data_out0 = 4'h7;
parameter data_out_end = 4'h8;
//----------------- main statemachine ------------------------------
parameter idle =4'h0;
parameter addr_read =4'h1;
parameter write_read_flag =4'h2;
parameter addr_ack =4'h3;
parameter data_write =4'h4;
parameter data_in_ack =4'h5;
parameter data_read =4'h6;
parameter data_out_ack =4'h7;
parameter reg_addr_read =4'h8;
parameter reg_addr_ack =4'h9;
parameter if_rep_start =4'ha;
//------------------------------------------------------------------
//main state machine
always @(posedge clock or negedge reset_n2)
if(!reset_n2)
begin
main_state <= idle;
write_read <= 1'b0;
end
else
begin
case (main_state)
idle:
begin
if(start_bus_reg) // receive start from SDA
begin
main_state <= addr_read;
end
else
begin
main_state <= idle;
end
end
addr_read: // read chip_id from the master
begin
if(addr_in_state==addr_end)
main_state <= write_read_flag;
else
main_state <= addr_read;
end
write_read_flag: // read R/W flag following chip_id
begin
if({scl_regi0,scl_regi,scl}==3'b011)
begin
write_read <= sda_in;
main_state <= addr_ack;
end
else
main_state <= write_read_flag;
end
addr_ack: // send chip_id_ack to the master
begin
if({scl_regi0,scl_regi,scl}==3'b011)
begin
if(addr_in_reg==7'b1100110)
main_state <= reg_addr_read;
else
main_state <= idle;
end
else
main_state <= addr_ack;
end
reg_addr_read: // read register address form master
begin
if(reg_addr_state==reg_addr_end)
main_state <= reg_addr_ack;
else
main_state <= reg_addr_read;
end
reg_addr_ack: // send reg_addr_ack to master
begin
if({scl_regi0,scl_regi,scl}==3'b011)
begin
if(sda_out1)
main_state <= idle;
else
begin
if(write_read) // '1': read
main_state <= data_read;
else // '0': write
main_state <= data_write;
end
end
else
main_state <= reg_addr_ack;
end
data_write: // read data from master
begin
if(data_in_state == data_end)
main_state <= data_in_ack;
else
main_state <= data_write;
end
data_in_ack: // write data_in_ack to master
begin
if({scl_regi0,scl_regi,scl}==3'b011)
main_state <= if_rep_start;
else
main_state <= data_in_ack;
end
data_read: // write data to master
begin
if(data_out_state==data_out_end && {scl_regi0,scl_regi,scl}==3'b100)
begin
main_state <= data_out_ack;
end
else
begin
main_state <= data_read;
end
end
data_out_ack: // write data_out_ack to master
begin
if({scl_regi0,scl_regi,scl}==3'b011)
main_state <= if_rep_start;
else
main_state <= data_out_ack;
end
if_rep_start: // read restart from master
begin
if(stop_bus_reg)
main_state <= idle;
else if(start_bus_reg)
main_state <= reg_addr_read;
else
main_state <= if_rep_start;
end
default: main_state <= idle;
endcase
end
//------------------------------------------------------------------
// send chip_id_ack to master
always @(posedge clock or negedge reset_n2) //addr ack output
begin
if(!reset_n2)
begin
ack_state <= 2'b00;
sda_en <= 1'b0;
flag <= 1'b0;
sda_out1 <= 1'b0;
end
else
begin
case(ack_state)
2'b00:
begin
if(main_state==addr_ack && {scl_regi0,scl_regi,scl}==3'b100) //to ack chip address
begin
if(addr_in_reg==7'b1100110)
sda_out1 <= 1'b0;
else
sda_out1 <= 1'b1;
flag <= 1'b0; //??1
sda_en <= 1'b1;
ack_state <= 2'b11;
end
else if(main_state==reg_addr_ack && {scl_regi0,scl_regi,scl}==3'b100)// to ack register address
begin
case(reg_addr)
8'haa: sda_out1 <= 1'b0;
8'h55: sda_out1 <= 1'b0;
default:sda_out1 <= 1'b1;
endcase
flag <= 1'b0; //??1
sda_en <= 1'b1;
ack_state <= 2'b11;
end
else if(main_state==data_in_ack && {scl_regi0,scl_regi,scl}==3'b100)
begin
flag <= 1'b0; //??1
sda_out1 <= 1'b0; //??2
sda_en <= 1'b1;
ack_state <= 2'b01;
end
else if(main_state==data_read && {scl_regi0,scl_regi,scl}==3'b100)
begin
flag <= 1'b1;
sda_en <= 1'b1;
ack_state <= 2'b10; //?master??????ack???
end
else
sda_en<=1'b0;
end
2'b01:
begin
if({scl_regi0,scl_regi,scl}==3'b100)
begin
sda_en <= 1'b0;
ack_state <= 2'b00;
end
else
ack_state <= 2'b01;
end
2'b10:
begin
if(main_state==data_read)
ack_state <= 2'b10;
else
begin
ack_state <= 2'b00;
sda_en <= 1'b0;
flag <= 1'b0;
end
end
2'b11:
begin
if(main_state==data_read && {scl_regi0,scl_regi,scl}==3'b100)
begin
flag <= 1'b1;
sda_en <= 1'b1;
ack_state <= 2'b10;
end
else if(main_state!=data_read && {scl_regi0,scl_regi,scl}==3'b100)
begin
ack_state <= 2'b00;
sda_en <= 1'b0;
end
else
ack_state <= 2'b11;
end
default: ack_state <= 2'b00;
endcase
end
end
//------------------------------------------------------------------
//to read Chip_id from master
always @(posedge clock or negedge reset_n2)//to write chip address
if(!reset_n2)
begin
addr_in_state <= addr_in6;
addr_in_reg <= 7'b0000000;
end
else if(main_state==addr_read)
begin
case(addr_in_state)
addr_in6:
begin
if({scl_regi0,scl_regi,scl}==3'b011)
begin
addr_in_state <= addr_in5;
addr_in_reg[6] <= sda_in;
end
else
addr_in_state <= addr_in6;
end
addr_in5:
begin
if({scl_regi0,scl_regi,scl}==3'b011)
begin
addr_in_state <= addr_in4;
addr_in_reg[5] <= sda_in;
end
else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -