📄 i2c_slave.v
字号:
4'd2: sda_out = out_reg[5];
4'd3: sda_out = out_reg[4];
4'd4: sda_out = out_reg[3];
4'd5: sda_out = out_reg[2];
4'd6: sda_out = out_reg[1];
4'd7: sda_out = out_reg[0];
default: sda_out = out_reg[0];
endcase
//----------------------------------------
//----------------------------------------
// Input De-Mux
always @ (posedge clk or negedge rst_l)
begin
if (!rst_l) begin
in_reg <= 8'h00;
end
else if (in_reg_enable) begin
case (bit_counter)
3'd0: in_reg[7] <= sda_reg_delayed;
3'd1: in_reg[6] <= sda_reg_delayed;
3'd2: in_reg[5] <= sda_reg_delayed;
3'd3: in_reg[4] <= sda_reg_delayed;
3'd4: in_reg[3] <= sda_reg_delayed;
3'd5: in_reg[2] <= sda_reg_delayed;
3'd6: in_reg[1] <= sda_reg_delayed;
3'd7: in_reg[0] <= sda_reg_delayed;
endcase
end
else begin
in_reg <= in_reg;
end
end
//----------------------------------------
//----------------------------------------
// I2C Slave State Machine
always @ (posedge clk or negedge rst_l)
begin
if (!rst_l) begin
current_state <= #1 idle;
end
else begin
case (current_state)
idle: begin
if (start_detect && scl) begin
current_state <= #1 start;
end
else begin
current_state <= #1 idle;
in_reg_enable <= #1 0;
end
end
start: begin
if (start_detect && scl) begin
current_state <= #1 start;
end
if (stop_detect && scl) begin
current_state <= #1 idle;
end
else if (scl_pulse) begin
bit_counter <= #1 0;
in_reg_enable <= #1 1;
end
else if (in_reg_enable ) begin
in_reg_enable = #1 0;
bit_counter = #1 bit_counter + 1;
current_state = #1 address;
// clear all the address hit flags
hit_7 <= #1 0;
hit_10_upper <= #1 0;
hit_10_lower <= #1 0;
word_add_flag <= #1 0;
ack_flag <= #1 0;
end
end
address: begin
if (start_detect && scl) begin
current_state <= #1 start;
end
else if (stop_detect && scl) begin
current_state <= #1 idle;
end
else if (scl_pulse && (bit_counter <= 8)) begin
in_reg_enable <= #1 1;
end
else if (in_reg_enable) begin
in_reg_enable <= #1 0;
bit_counter <= #1 bit_counter + 1;
current_state <= #1 address;
end
else if (bit_counter == 8 && address_mode == `seven_bit) begin
// determine if r or w and set r_w_bit
if (in_reg[7:1] == address_reg_7) begin
r_w_bit <= #1 in_reg[0];
current_state <= #1 ack;
hit_7 <= #1 1;
ack_flag <= #1 0; // used in ack state
end
else begin
// the address is not for this slave
sda_out <= #1 1'b1;
current_state <= #1 idle;
end
end
// check if upper address byte is a hit in 10 bit addressing
else if (bit_counter == 8 && address_mode == `ten_bit && hit_10_upper == 0)
begin
// first time checking upper hit
if (in_reg[7:1] == address_reg_10_upper) begin
r_w_bit <= #1 in_reg[0];
current_state <= #1 ack;
hit_10_upper <= #1 1;
ack_flag <= #1 0; // used in ack state
if (in_reg[0] == 1'b0) begin
temp_add_upper <= #1 in_reg[7:1];
read_10_flag <= #1 0; // clear
end
else if ((in_reg[0] === 1'b1) && (temp_add_upper === in_reg[7:1]) && (temp_add_lower === address_reg_10_lower) )
begin
// This flag is set because the last 10 bit addressing
// mode write was for this slave, so this read only
// requires a match of the first byte of addressing
read_10_flag <= #1 1; // set
end
end
else begin
// the address is not for this slave
sda_out <= #1 1'b1;
current_state <= #1 idle;
temp_add_upper = in_reg[7:1]; // holds value of last
// upper add
read_10_flag <= #1 0; // clear
end
end
// check if lower address byte is a hit in 10 bit addressing
else if (bit_counter == 8 && address_mode==`ten_bit && hit_10_upper == 1)
begin
// is the lower address a hit?
if (in_reg[7:0] == address_reg_10_lower) begin
current_state <= #1 ack;
hit_10_lower <= #1 1;
ack_flag <= #1 0; // used in ack state
temp_add_lower <= #1 in_reg[7:0];
end
else begin
// the address is not for this slave
sda_out <= #1 1'b1;
current_state <= #1 idle;
temp_add_lower <= #1 in_reg[7:0];
end
end
end
ack: begin
// starts with scl high
// if we get a start goto start
if (start_detect && scl) begin
current_state <= #1 start;
end
// if there is a stop goto idle
else if (stop_detect && scl) begin
current_state <= #1 idle;
end
// if there is an address hit acknowledge the address
else if ((hit_7 || hit_10_upper || hit_10_lower) && scl_neg_pulse && !ack_flag) begin
out_en <= #tdh 1; // turn on OE
ack_flag <= #1 1;
end
// once the acknowledge is presented turn off the OE
// print the address, and goto address or data depending on
// addressing mode
else if ((hit_7 || hit_10_upper || hit_10_lower) && scl_neg_pulse && ack_flag ) begin
out_en <= #tdh 0; // #1 0; // turn off OE
bit_counter <= #1 4'b0;
if (hit_10_upper && !hit_10_lower) begin
$display($time, " %m << 10 bit addressing Upper address is %b >>", in_reg);
if (read_10_flag == 1'b0) begin
current_state <= #1 address;
end
else if (read_10_flag === 1'b1) begin
// going to the data state because a read in 10 bit
// addressing only requires a hit on the upper address if
// the last write was a hit.
current_state <= #1 data;
end
end
else if (hit_7 || hit_10_lower) begin
// hit_10_lower or hit_7
current_state <= #1 data;
if (hit_10_lower) begin
$display($time, " %m << 10 bit addressing Lower address is %b >>", in_reg);
end
else if (hit_7) begin
$display($time, " %m << 7 bit addressing & address is %b >>", in_reg);
end
end
end //((hit_7 || hit_10_upper || hit_10_lower) && scl_neg_pulse && ack_flag ) begin
// if there is no hit, return to idle
else if (!hit_7 && !hit_10_upper && !hit_10_lower) begin
// no_ack
out_en = #1 1'b0;
bit_counter <= #1 4'b0;
current_state <= #1 idle;
end
end
data: begin
// starts with scl low
if (start_detect && scl) begin
current_state <= #1 start;
end
else if (stop_detect && scl) begin
current_state <= #1 idle;
end
// write data
else begin // outer else
if (!r_w_bit && scl_pulse && (bit_counter <= 8) ) begin
// write
in_reg_enable <= #1 1;
end
else if (!r_w_bit && in_reg_enable && (bit_counter <= 8)) begin
// write more
in_reg_enable <= #1 0;
bit_counter <= #1 bit_counter + 1;
current_state <= #1 data;
end
else if (!r_w_bit && (bit_counter == 8)) begin
// write last bit
in_reg_enable <= #1 0; // disable
current_state <= #1 data_ack;
ack_flag <= #1 0; // used in data_ack state
if (!word_add_flag) begin
word_address <= #1 in_reg;
word_add_flag <= #1 1; // set the flag
end
else begin
mem[word_address] <= #1 in_reg;
word_address <= #1 word_address + 1;
end
end
// read data
else if (r_w_bit && (bit_counter == 0) && !scl_neg_pulse) begin
// read first bit of word
// scl is low at start of read
out_en <=#tdh 1; // turn on OE
out_reg <= #tdh mem[word_address];
end
else if (r_w_bit && (bit_counter < 7) && scl_neg_pulse) begin
// set up next bit
bit_counter <= #tdh bit_counter + 1;
end
else if (r_w_bit && (bit_counter == 7) && scl_neg_pulse) begin
// we already output the last bit
bit_counter <= #tdh 0;
out_en <= #tdh 0; // turn off OE
word_address <= #1 word_address + 1;
current_state <= #1 data_ack;
ack_flag <= #1 0; // used in data_ack state
end
end // outer else
end // end data phase
data_ack: begin
if (start_detect && scl) begin
current_state <= #1 start;
end
else if (stop_detect && scl) begin
current_state <= #1 idle;
end
// starts with scl high on write
else if ( !r_w_bit && scl_neg_pulse && !ack_flag) begin
$display($time, " %m << Slave Data Received on write is %b >>", in_reg);
out_en <= #10 1; // turn on OE
sda_out <= #20 0; // sda is 0 so ack
ack_flag <= #1 1;
end
else if ( !r_w_bit && scl_neg_pulse && ack_flag ) begin
out_en <= #10 0; // turn off OE
sda_out <= #20 1; // sda is becomes a Z
bit_counter <= #1 4'b0;
current_state <= #1 data;
end
// starts with scl low on read
else if (r_w_bit && scl_pulse) begin
// check sda for ack now
$display($time, " %m << Slave Data transmitted on read is %b >>", out_reg);
if (!sda) begin
next_state <= #1 data;
bit_counter <= #1 4'b0;
$display($time, " %m Master ACK'd on a Data Read, returning to Data ");
ack_flag <= #1 1;
end
else if (sda) begin
$display($time, " %m No ACK on a Data Read, returning to Idle ");
next_state <= #1 idle;
ack_flag <= #1 1;
end
end
else if (r_w_bit && scl_neg_pulse) begin
current_state <= #1 next_state;
end
end
default: begin
if (start_detect && scl) begin
current_state <= #1 start;
end
else if (stop_detect && scl) begin
current_state <= #1 idle;
end
else begin
current_state <= #1 idle;
$display($time, " %m Something is broken is the SM returning to idle");
end
end // end of default
endcase
end // end of else
end // end of always
endmodule
//------------------------------- E O F -------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -