Files
retroSoC/rtl/ip/rib/serial/i2c_core.sv
T
maksyuki c5dfa96c6f feat: refactor NMI to RIB Interface and Update Related Tests
- Replaced instances of nmi_if with rib_if in sysctrl_fault_tb.sv, test_dma.py, test_gpio_user_mux.py, test_memory_map.py, test_performance_log.py, test_script_tools.py, test_sdram.py, test_soc_topology.py, test_sta_constraints.py, and test_user_extensions.py.
- Updated test cases to reflect changes from NMI to RIB, ensuring all references and assertions are consistent with the new interface.
- Adjusted memory map and performance log tests to validate RIB-specific configurations and behaviors.
- Ensured that all relevant file paths and definitions are updated to maintain functionality and correctness across the test suite.
2026-08-02 15:57:08 +08:00

387 lines
11 KiB
Systemverilog

// Copyright 2019 EmbedFire http://www.embedfire.com
// https://github.com/Embedfire-altera <embedfire@embedfire.com>
//
// The first version of this code was derived from EmbedFire i2c_ctrl.v. The
// original code is open source on Github, but it doesn't specify an open-source
// license. I'm re-releasing it here under the most compatible license(PSL License).
// If anyone knows what the original license is, please contact <miaoyuchi@ict.ac.cn>.
//
// -- Adaptable modifications are redistributed under compatible License --
//
// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
module i2c_core (
input logic clk_i,
input logic rst_n_i,
input logic [ 6:0] clk_div_i,
input logic [ 6:0] dev_addr_i,
input logic rdwr_i,
input logic start_i,
output logic end_o,
input logic extn_addr_i,
input logic [15:0] reg_addr_i,
input logic [ 7:0] wr_data_i,
output logic [ 7:0] rd_data_o,
output logic oper_clk_pos_o,
output logic scl_o,
output logic sda_oe_o,
output logic sda_o,
input logic sda_i
);
localparam FSM_IDLE = 4'd0;
localparam FSM_START_F = 4'd1;
localparam FSM_SEND_DEV_ADDR = 4'd2;
localparam FSM_ACK_1 = 4'd3;
localparam FSM_SEND_REG_ADDR_H = 4'd4;
localparam FSM_ACK_2 = 4'd5;
localparam FSM_SEND_REG_ADDR_L = 4'd6;
localparam FSM_ACK_3 = 4'd7;
localparam FSM_WR_DATA = 4'd8;
localparam FSM_ACK_4 = 4'd9;
localparam FSM_START_S = 4'd10;
localparam FSM_SEND_RD_ADDR = 4'd11;
localparam FSM_ACK_5 = 4'd12;
localparam FSM_RD_DATA = 4'd13;
localparam FSM_N_ACK = 4'd14;
localparam FSM_STOP = 4'd15;
// oper clk: i2c clk = 4:1
logic s_rdwr_d, s_rdwr_q;
logic [6:0] s_oper_clk_cnt_d, s_oper_clk_cnt_q;
logic s_oper_clk_d, s_oper_clk_q;
logic s_oper_clk_pos;
logic s_i2c_clk_en_d, s_i2c_clk_en_q;
logic [1:0] s_i2c_clk_cnt_d, s_i2c_clk_cnt_q;
logic [2:0] s_xfer_bit_cnt_d, s_xfer_bit_cnt_q;
logic [3:0] s_fsm_d, s_fsm_q;
logic s_i2c_sda_out;
logic [7:0] s_i2c_data_in_d, s_i2c_data_in_q;
logic s_i2c_ack_d, s_i2c_ack_q;
logic s_xfer_end_d, s_xfer_end_q;
assign s_oper_clk_pos = (~s_oper_clk_q) & (s_oper_clk_cnt_q == clk_div_i);
assign oper_clk_pos_o = s_oper_clk_pos;
assign end_o = s_xfer_end_q;
assign rd_data_o = s_i2c_data_in_q;
// verilog_format: off
assign sda_o = s_i2c_sda_out;
assign sda_oe_o = ((s_fsm_q == FSM_RD_DATA) || (s_fsm_q == FSM_ACK_1) || (s_fsm_q == FSM_ACK_2)
|| (s_fsm_q == FSM_ACK_3) || (s_fsm_q == FSM_ACK_4) || (s_fsm_q == FSM_ACK_5))
? 1'b0 : 1'b1;
// verilog_format: on
always_comb begin
s_oper_clk_cnt_d = s_oper_clk_cnt_q;
s_oper_clk_d = s_oper_clk_q;
if (s_oper_clk_cnt_q == clk_div_i) begin
s_oper_clk_cnt_d = '0;
s_oper_clk_d = ~s_oper_clk_q;
end else begin
s_oper_clk_cnt_d = s_oper_clk_cnt_q + 1'b1;
end
end
dffr #(7) u_oper_clk_cnt_dffr (
clk_i,
rst_n_i,
s_oper_clk_cnt_d,
s_oper_clk_cnt_q
);
dffrh #(1) u_oper_clk_dffrh (
clk_i,
rst_n_i,
s_oper_clk_d,
s_oper_clk_q
);
always_comb begin
s_i2c_clk_en_d = s_i2c_clk_en_q;
if (start_i) s_i2c_clk_en_d = 1'b1;
else if ((s_fsm_q == FSM_STOP) && (s_xfer_bit_cnt_q == 3'd3) && (s_i2c_clk_cnt_q == 2'd3)) begin
s_i2c_clk_en_d = 1'b0;
end
end
dffer #(1) u_i2c_clk_en_dffer (
clk_i,
rst_n_i,
s_oper_clk_pos,
s_i2c_clk_en_d,
s_i2c_clk_en_q
);
assign s_i2c_clk_cnt_d = s_i2c_clk_en_q ? s_i2c_clk_cnt_q + 1'b1 : s_i2c_clk_cnt_q;
dffer #(2) u_i2c_clk_cnt_dffer (
clk_i,
rst_n_i,
s_oper_clk_pos,
s_i2c_clk_cnt_d,
s_i2c_clk_cnt_q
);
always_comb begin
s_xfer_bit_cnt_d = s_xfer_bit_cnt_q;
if((s_fsm_q == FSM_IDLE) || (s_fsm_q == FSM_START_F) || (s_fsm_q == FSM_START_S)
|| (s_fsm_q == FSM_ACK_1) || (s_fsm_q == FSM_ACK_2) || (s_fsm_q == FSM_ACK_3)
|| (s_fsm_q == FSM_ACK_4) || (s_fsm_q == FSM_ACK_5) || (s_fsm_q == FSM_N_ACK)) begin
s_xfer_bit_cnt_d = '0;
end else if ((s_xfer_bit_cnt_q == 3'd7) && (s_i2c_clk_cnt_q == 2'd3)) begin
s_xfer_bit_cnt_d = '0;
end else if ((s_i2c_clk_cnt_q == 2'd3) && (s_fsm_q != FSM_IDLE)) begin
s_xfer_bit_cnt_d = s_xfer_bit_cnt_q + 1'b1;
end
end
dffer #(3) u_xfer_bit_cnt_dffer (
clk_i,
rst_n_i,
s_oper_clk_pos,
s_xfer_bit_cnt_d,
s_xfer_bit_cnt_q
);
always_comb begin
s_fsm_d = s_fsm_q;
unique case (s_fsm_q)
FSM_IDLE: begin
if (start_i == 1'b1) s_fsm_d = FSM_START_F;
end
FSM_START_F: begin
if (s_i2c_clk_cnt_q == 2'd3) s_fsm_d = FSM_SEND_DEV_ADDR;
end
FSM_SEND_DEV_ADDR: begin
if ((s_xfer_bit_cnt_q == 3'd7) && (s_i2c_clk_cnt_q == 2'd3)) begin
s_fsm_d = FSM_ACK_1;
end
end
FSM_ACK_1: begin
if ((s_i2c_clk_cnt_q == 2'd3) && (s_i2c_ack_q == 1'b0)) begin
if (extn_addr_i == 1'b1) s_fsm_d = FSM_SEND_REG_ADDR_H;
else s_fsm_d = FSM_SEND_REG_ADDR_L;
end
end
FSM_SEND_REG_ADDR_H: begin
if ((s_xfer_bit_cnt_q == 3'd7) && (s_i2c_clk_cnt_q == 2'd3)) begin
s_fsm_d = FSM_ACK_2;
end
end
FSM_ACK_2: begin
if ((s_i2c_clk_cnt_q == 2'd3) && (s_i2c_ack_q == 1'b0)) begin
s_fsm_d = FSM_SEND_REG_ADDR_L;
end
end
FSM_SEND_REG_ADDR_L: begin
if ((s_xfer_bit_cnt_q == 3'd7) && (s_i2c_clk_cnt_q == 2'd3)) begin
s_fsm_d = FSM_ACK_3;
end
end
FSM_ACK_3: begin
if ((s_i2c_clk_cnt_q == 2'd3) && (s_i2c_ack_q == 1'b0)) begin
if (~s_rdwr_q) s_fsm_d = FSM_WR_DATA;
else s_fsm_d = FSM_START_S;
end
end
FSM_WR_DATA: begin
if ((s_xfer_bit_cnt_q == 3'd7) && (s_i2c_clk_cnt_q == 2'd3)) begin
s_fsm_d = FSM_ACK_4;
end
end
FSM_ACK_4: begin
if ((s_i2c_clk_cnt_q == 2'd3) && (s_i2c_ack_q == 1'b0)) begin
s_fsm_d = FSM_STOP;
end
end
FSM_START_S: begin
if (s_i2c_clk_cnt_q == 2'd3) s_fsm_d = FSM_SEND_RD_ADDR;
end
FSM_SEND_RD_ADDR: begin
if ((s_xfer_bit_cnt_q == 3'd7) && (s_i2c_clk_cnt_q == 2'd3)) begin
s_fsm_d = FSM_ACK_5;
end
end
FSM_ACK_5: begin
if ((s_i2c_clk_cnt_q == 2'd3) && (s_i2c_ack_q == 1'b0)) begin
s_fsm_d = FSM_RD_DATA;
end
end
FSM_RD_DATA: begin
if ((s_xfer_bit_cnt_q == 3'd7) && (s_i2c_clk_cnt_q == 2'd3)) begin
s_fsm_d = FSM_N_ACK;
end
end
FSM_N_ACK: begin
if (s_i2c_clk_cnt_q == 2'd3) s_fsm_d = FSM_STOP;
end
FSM_STOP: begin
if ((s_xfer_bit_cnt_q == 3'd3) && (s_i2c_clk_cnt_q == 2'd3)) begin
s_fsm_d = FSM_IDLE;
end
end
default: s_fsm_d = FSM_IDLE;
endcase
end
dffer #(4) u_fsm_dffer (
clk_i,
rst_n_i,
s_oper_clk_pos,
s_fsm_d,
s_fsm_q
);
always_comb begin
s_i2c_ack_d = s_i2c_ack_q;
if((s_fsm_q == FSM_ACK_1) || (s_fsm_q == FSM_ACK_2) || (s_fsm_q == FSM_ACK_3)
|| (s_fsm_q == FSM_ACK_4) || (s_fsm_q == FSM_ACK_5)) begin
if (s_i2c_clk_cnt_q == 2'd1) s_i2c_ack_d = sda_i;
else if (s_i2c_clk_cnt_q == 2'd3) s_i2c_ack_d = 1'b1;
end
end
dfferh #(1) u_ack_dfferh (
clk_i,
rst_n_i,
s_oper_clk_pos,
s_i2c_ack_d,
s_i2c_ack_q
);
always_comb begin
s_rdwr_d = s_rdwr_q;
if (s_fsm_q == FSM_IDLE && start_i) begin
s_rdwr_d = rdwr_i;
end
end
dffer #(1) u_rdwr_dffer (
clk_i,
rst_n_i,
s_oper_clk_pos,
s_rdwr_d,
s_rdwr_q
);
always_comb begin
scl_o = 1'b1;
unique case (s_fsm_q)
FSM_IDLE: scl_o = 1'b1;
FSM_START_F: begin
if (s_i2c_clk_cnt_q == 2'd3) scl_o = 1'b0;
else scl_o = 1'b1;
end
FSM_SEND_DEV_ADDR,FSM_ACK_1,FSM_SEND_REG_ADDR_H,FSM_ACK_2,FSM_SEND_REG_ADDR_L,
FSM_ACK_3,FSM_WR_DATA,FSM_ACK_4,FSM_START_S,FSM_SEND_RD_ADDR,FSM_ACK_5,FSM_RD_DATA,FSM_N_ACK: begin
if ((s_i2c_clk_cnt_q == 2'd1) || (s_i2c_clk_cnt_q == 2'd2)) scl_o = 1'b1;
else scl_o = 1'b0;
end
FSM_STOP: begin
if ((s_xfer_bit_cnt_q == 3'd0) && (s_i2c_clk_cnt_q == 2'd0)) scl_o = 1'b0;
else scl_o = 1'b1;
end
default: scl_o = 1'b1;
endcase
end
always_comb begin
s_i2c_sda_out = 1'b1;
unique case (s_fsm_q)
FSM_IDLE: begin
s_i2c_sda_out = 1'b1;
end
FSM_START_F: begin
if (s_i2c_clk_cnt_q == 2'd0) s_i2c_sda_out = 1'b1;
else s_i2c_sda_out = 1'b0;
end
FSM_SEND_DEV_ADDR: begin
if (s_xfer_bit_cnt_q <= 3'd6) begin
s_i2c_sda_out = dev_addr_i[6-s_xfer_bit_cnt_q];
end else s_i2c_sda_out = 1'b0;
end
FSM_ACK_1: begin
s_i2c_sda_out = 1'b1;
end
FSM_SEND_REG_ADDR_H: begin
s_i2c_sda_out = reg_addr_i[15-s_xfer_bit_cnt_q];
end
FSM_ACK_2: begin
s_i2c_sda_out = 1'b1;
end
FSM_SEND_REG_ADDR_L: begin
s_i2c_sda_out = reg_addr_i[7-s_xfer_bit_cnt_q];
end
FSM_ACK_3: begin
s_i2c_sda_out = 1'b1;
end
FSM_WR_DATA: begin
s_i2c_sda_out = wr_data_i[7-s_xfer_bit_cnt_q];
end
FSM_ACK_4: begin
s_i2c_sda_out = 1'b1;
end
FSM_START_S: begin
if (s_i2c_clk_cnt_q <= 2'd1) s_i2c_sda_out = 1'b1;
else s_i2c_sda_out = 1'b0;
end
FSM_SEND_RD_ADDR: begin
if (s_xfer_bit_cnt_q <= 3'd6) begin
s_i2c_sda_out = dev_addr_i[6-s_xfer_bit_cnt_q];
end else s_i2c_sda_out = 1'b1;
end
FSM_ACK_5: begin
s_i2c_sda_out = 1'b1;
end
FSM_RD_DATA: begin
s_i2c_sda_out = 1'b1;
end
FSM_N_ACK: begin
s_i2c_sda_out = 1'b1;
end
FSM_STOP: begin
if ((s_xfer_bit_cnt_q == 3'd0) && (s_i2c_clk_cnt_q < 2'd3)) s_i2c_sda_out = 1'b0;
else s_i2c_sda_out = 1'b1;
end
default: begin
s_i2c_sda_out = 1'b1;
end
endcase
end
always_comb begin
s_i2c_data_in_d = s_i2c_data_in_q;
if (s_fsm_q == FSM_RD_DATA && s_i2c_clk_cnt_q == 2'd1) begin
s_i2c_data_in_d[7-s_xfer_bit_cnt_q] = sda_i;
end
end
dffer #(8) u_i2c_data_in_dffer (
clk_i,
rst_n_i,
s_oper_clk_pos,
s_i2c_data_in_d,
s_i2c_data_in_q
);
always_comb begin
if ((s_fsm_q == FSM_STOP) && (s_xfer_bit_cnt_q == 3'd3) && (s_i2c_clk_cnt_q == 3)) begin
s_xfer_end_d = 1'b1;
end else s_xfer_end_d = 1'b0;
end
dffer #(1) u_xfer_end_dffer (
clk_i,
rst_n_i,
s_oper_clk_pos,
s_xfer_end_d,
s_xfer_end_q
);
endmodule