rtl:perips: add i2c master
Signed-off-by: liangkangnan <liangkangnan@163.com>pull/4/head
parent
6143d9ee6a
commit
2afcba47ea
|
@ -53,6 +53,11 @@
|
||||||
../rtl/perips/rvic/rvic_reg_top.sv
|
../rtl/perips/rvic/rvic_reg_top.sv
|
||||||
../rtl/perips/rvic/rvic_core.sv
|
../rtl/perips/rvic/rvic_core.sv
|
||||||
../rtl/perips/rvic/rvic_top.sv
|
../rtl/perips/rvic/rvic_top.sv
|
||||||
|
../rtl/perips/i2c/i2c_reg_pkg.sv
|
||||||
|
../rtl/perips/i2c/i2c_reg_top.sv
|
||||||
|
../rtl/perips/i2c/i2c_core.sv
|
||||||
|
../rtl/perips/i2c/i2c_top.sv
|
||||||
|
../rtl/perips/i2c/i2c_master.sv
|
||||||
|
|
||||||
../rtl/sys_bus/obi_interconnect.sv
|
../rtl/sys_bus/obi_interconnect.sv
|
||||||
../rtl/sys_bus/obi_interconnect_master_sel.sv
|
../rtl/sys_bus/obi_interconnect_master_sel.sv
|
||||||
|
|
|
@ -40,6 +40,9 @@
|
||||||
// UART0
|
// UART0
|
||||||
`define UART0_ADDR_MASK ~32'hffff
|
`define UART0_ADDR_MASK ~32'hffff
|
||||||
`define UART0_ADDR_BASE 32'h50000000
|
`define UART0_ADDR_BASE 32'h50000000
|
||||||
|
// I2C0
|
||||||
|
`define I2C0_ADDR_MASK ~32'hffff
|
||||||
|
`define I2C0_ADDR_BASE 32'h60000000
|
||||||
// Interrupt controller
|
// Interrupt controller
|
||||||
`define RVIC_ADDR_MASK ~32'hffff
|
`define RVIC_ADDR_MASK ~32'hffff
|
||||||
`define RVIC_ADDR_BASE 32'hD0000000
|
`define RVIC_ADDR_BASE 32'hD0000000
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
// Copyright lowRISC contributors.
|
||||||
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
{ name: "i2c",
|
||||||
|
clocking: [{clock: "clk_i", reset: "rst_ni"}],
|
||||||
|
bus_interfaces: [
|
||||||
|
{ protocol: "tlul", direction: "device" }
|
||||||
|
],
|
||||||
|
regwidth: "32",
|
||||||
|
registers: [
|
||||||
|
{ name: "CTRL",
|
||||||
|
desc: "I2C control register",
|
||||||
|
swaccess: "rw",
|
||||||
|
hwaccess: "hrw",
|
||||||
|
hwqe: "true",
|
||||||
|
fields: [
|
||||||
|
{ bits: "0",
|
||||||
|
name: "START",
|
||||||
|
desc: "I2C start",
|
||||||
|
}
|
||||||
|
{ bits: "1",
|
||||||
|
name: "INT_EN",
|
||||||
|
desc: "I2C interrupt enable",
|
||||||
|
}
|
||||||
|
{ bits: "2",
|
||||||
|
name: "INT_PENDING",
|
||||||
|
swaccess: "rw1c",
|
||||||
|
desc: "I2C interrupt pending",
|
||||||
|
}
|
||||||
|
{ bits: "3",
|
||||||
|
name: "MODE",
|
||||||
|
desc: "I2C mode, 0: master, 1: slave",
|
||||||
|
}
|
||||||
|
{ bits: "4",
|
||||||
|
name: "WRITE",
|
||||||
|
desc: "0: write, 1: read",
|
||||||
|
}
|
||||||
|
{ bits: "5",
|
||||||
|
name: "ACK",
|
||||||
|
swaccess: "ro",
|
||||||
|
desc: "0: ack, 1: nack",
|
||||||
|
}
|
||||||
|
{ bits: "6",
|
||||||
|
name: "ERROR",
|
||||||
|
swaccess: "ro",
|
||||||
|
desc: "0: no error, 1: error",
|
||||||
|
}
|
||||||
|
{ bits: "31:16",
|
||||||
|
name: "CLK_DIV",
|
||||||
|
desc: "I2C clock divider count",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{ name: "MASTER_DATA",
|
||||||
|
desc: "I2C master transfer data register",
|
||||||
|
swaccess: "rw",
|
||||||
|
hwaccess: "hrw",
|
||||||
|
fields: [
|
||||||
|
{ bits: "7:0",
|
||||||
|
name: "ADDRESS",
|
||||||
|
desc: "I2C slave address",
|
||||||
|
}
|
||||||
|
{ bits: "15:8",
|
||||||
|
name: "REGREG",
|
||||||
|
desc: "I2C write or read reg",
|
||||||
|
}
|
||||||
|
{ bits: "23:16",
|
||||||
|
name: "DATA",
|
||||||
|
desc: "I2C write or read data",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{ name: "SLAVE_DATA",
|
||||||
|
desc: "I2C slave received data register",
|
||||||
|
swaccess: "ro",
|
||||||
|
hwaccess: "hrw",
|
||||||
|
hwext: "true",
|
||||||
|
hwre: "true",
|
||||||
|
fields: [
|
||||||
|
{ bits: "7:0",
|
||||||
|
desc: "I2C slave received data(fifo)",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 Blue Liang, liangkangnan@163.com
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module i2c_core (
|
||||||
|
input logic clk_i,
|
||||||
|
input logic rst_ni,
|
||||||
|
|
||||||
|
output logic scl_o,
|
||||||
|
output logic scl_oe_o,
|
||||||
|
input logic scl_i,
|
||||||
|
output logic sda_o,
|
||||||
|
output logic sda_oe_o,
|
||||||
|
input logic sda_i,
|
||||||
|
|
||||||
|
output logic irq_o,
|
||||||
|
|
||||||
|
input logic reg_we_i,
|
||||||
|
input logic reg_re_i,
|
||||||
|
input logic [31:0] reg_wdata_i,
|
||||||
|
input logic [ 3:0] reg_be_i,
|
||||||
|
input logic [31:0] reg_addr_i,
|
||||||
|
output logic [31:0] reg_rdata_o
|
||||||
|
);
|
||||||
|
|
||||||
|
import i2c_reg_pkg::*;
|
||||||
|
|
||||||
|
i2c_reg_pkg::i2c_reg2hw_t reg2hw;
|
||||||
|
i2c_reg_pkg::i2c_hw2reg_t hw2reg;
|
||||||
|
|
||||||
|
logic master_mode;
|
||||||
|
logic slave_mode;
|
||||||
|
logic op_write;
|
||||||
|
logic op_read;
|
||||||
|
logic start;
|
||||||
|
logic [15:0] clk_div;
|
||||||
|
logic int_enable;
|
||||||
|
logic [7:0] master_address;
|
||||||
|
logic [7:0] master_register;
|
||||||
|
logic [7:0] master_data;
|
||||||
|
logic master_ready, master_ready_q;
|
||||||
|
logic master_start;
|
||||||
|
logic master_error;
|
||||||
|
logic [7:0] master_read_data;
|
||||||
|
|
||||||
|
assign master_mode = ~reg2hw.ctrl.mode.q;
|
||||||
|
assign slave_mode = reg2hw.ctrl.mode.q;
|
||||||
|
assign op_write = ~reg2hw.ctrl.write.q;
|
||||||
|
assign op_read = reg2hw.ctrl.write.q;
|
||||||
|
assign start = reg2hw.ctrl.start.q;
|
||||||
|
assign clk_div = reg2hw.ctrl.clk_div.q;
|
||||||
|
assign int_enable = reg2hw.ctrl.int_en.q;
|
||||||
|
|
||||||
|
assign master_address = reg2hw.master_data.address.q;
|
||||||
|
assign master_register = reg2hw.master_data.regreg.q;
|
||||||
|
assign master_data = reg2hw.master_data.data.q;
|
||||||
|
|
||||||
|
// 软件写1启动master传输
|
||||||
|
assign master_start = reg2hw.ctrl.start.qe && reg2hw.ctrl.start.q && master_ready;
|
||||||
|
|
||||||
|
// master传输完成后,硬件清start位
|
||||||
|
assign hw2reg.ctrl.start.d = 1'b0;
|
||||||
|
// master传输完成上升沿脉冲
|
||||||
|
assign hw2reg.ctrl.start.de = (~master_ready_q) && master_ready;
|
||||||
|
|
||||||
|
// 传输完成产生中断pending
|
||||||
|
assign hw2reg.ctrl.int_pending.d = 1'b1;
|
||||||
|
assign hw2reg.ctrl.int_pending.de = int_enable && (~master_ready_q) && master_ready;
|
||||||
|
|
||||||
|
// 传输完成并且是读操作,则更新master data
|
||||||
|
assign hw2reg.master_data.data.d = master_read_data;
|
||||||
|
assign hw2reg.master_data.data.de = op_read && (~master_ready_q) && master_ready;
|
||||||
|
|
||||||
|
// 传输完成更新error
|
||||||
|
assign hw2reg.ctrl.error.d = master_error;
|
||||||
|
assign hw2reg.ctrl.error.de = (~master_ready_q) && master_ready;
|
||||||
|
|
||||||
|
assign irq_o = reg2hw.ctrl.int_pending.q;
|
||||||
|
|
||||||
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||||
|
if (~rst_ni) begin
|
||||||
|
master_ready_q <= 1'b1;
|
||||||
|
end else begin
|
||||||
|
master_ready_q <= master_ready;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
i2c_master u_i2c_master (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
.enable_i (master_mode),
|
||||||
|
.div_ratio_i (clk_div),
|
||||||
|
.read_i (op_read),
|
||||||
|
.slave_addr_i (master_address),
|
||||||
|
.slave_reg_i (master_register),
|
||||||
|
.slave_data_i (master_data),
|
||||||
|
.start_i (master_start),
|
||||||
|
.ready_o (master_ready),
|
||||||
|
.error_o (master_error),
|
||||||
|
.data_o (master_read_data),
|
||||||
|
.scl_i (scl_i),
|
||||||
|
.scl_o (scl_o),
|
||||||
|
.scl_oe_o (scl_oe_o),
|
||||||
|
.sda_i (sda_i),
|
||||||
|
.sda_o (sda_o),
|
||||||
|
.sda_oe_o (sda_oe_o)
|
||||||
|
);
|
||||||
|
|
||||||
|
i2c_reg_top u_i2c_reg_top (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
.reg2hw (reg2hw),
|
||||||
|
.hw2reg (hw2reg),
|
||||||
|
.reg_we (reg_we_i),
|
||||||
|
.reg_re (reg_re_i),
|
||||||
|
.reg_wdata (reg_wdata_i),
|
||||||
|
.reg_be (reg_be_i),
|
||||||
|
.reg_addr (reg_addr_i),
|
||||||
|
.reg_rdata (reg_rdata_o)
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
|
@ -0,0 +1,288 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 Blue Liang, liangkangnan@163.com
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module i2c_master (
|
||||||
|
input logic clk_i,
|
||||||
|
input logic rst_ni,
|
||||||
|
|
||||||
|
input logic enable_i,
|
||||||
|
input logic [15:0] div_ratio_i,
|
||||||
|
input logic read_i,
|
||||||
|
input logic [7:0] slave_addr_i,
|
||||||
|
input logic [7:0] slave_reg_i,
|
||||||
|
input logic [7:0] slave_data_i,
|
||||||
|
input logic start_i,
|
||||||
|
output logic ready_o,
|
||||||
|
output logic error_o,
|
||||||
|
output logic [7:0] data_o,
|
||||||
|
|
||||||
|
input logic scl_i,
|
||||||
|
output logic scl_o,
|
||||||
|
output logic scl_oe_o,
|
||||||
|
input logic sda_i,
|
||||||
|
output logic sda_o,
|
||||||
|
output logic sda_oe_o
|
||||||
|
);
|
||||||
|
|
||||||
|
localparam S_IDLE = 6'b000001;
|
||||||
|
localparam S_START = 6'b000010;
|
||||||
|
localparam S_ADDR = 6'b000100;
|
||||||
|
localparam S_REG = 6'b001000;
|
||||||
|
localparam S_DATA = 6'b010000;
|
||||||
|
localparam S_STOP = 6'b100000;
|
||||||
|
|
||||||
|
logic tick;
|
||||||
|
|
||||||
|
logic error_d, error_q;
|
||||||
|
logic [7:0] data_d, data_q;
|
||||||
|
logic [4:0] edge_cnt_d, edge_cnt_q;
|
||||||
|
logic [7:0] shift_reg_d, shift_reg_q;
|
||||||
|
logic [5:0] state_d, state_q;
|
||||||
|
logic sda_d, sda_q;
|
||||||
|
logic scl_d, scl_q;
|
||||||
|
logic sda_oe_d, sda_oe_q;
|
||||||
|
logic scl_oe_d, scl_oe_q;
|
||||||
|
logic op_read_d, op_read_q;
|
||||||
|
|
||||||
|
|
||||||
|
always_comb begin
|
||||||
|
state_d = state_q;
|
||||||
|
shift_reg_d = shift_reg_q;
|
||||||
|
scl_d = scl_q;
|
||||||
|
sda_oe_d = sda_oe_q;
|
||||||
|
scl_oe_d = scl_oe_q;
|
||||||
|
sda_d = sda_q;
|
||||||
|
edge_cnt_d = edge_cnt_q;
|
||||||
|
data_d = data_q;
|
||||||
|
error_d = error_q;
|
||||||
|
op_read_d = op_read_q;
|
||||||
|
|
||||||
|
if (!enable_i) begin
|
||||||
|
sda_d = 1'b0;
|
||||||
|
sda_oe_d = 1'b0;
|
||||||
|
scl_d = 1'b0;
|
||||||
|
scl_oe_d = 1'b0;
|
||||||
|
state_d = S_IDLE;
|
||||||
|
end else begin
|
||||||
|
case (state_q)
|
||||||
|
S_IDLE: begin
|
||||||
|
sda_d = 1'b1;
|
||||||
|
sda_oe_d = 1'b1;
|
||||||
|
scl_d = 1'b1;
|
||||||
|
scl_oe_d = 1'b1;
|
||||||
|
if (start_i) begin
|
||||||
|
state_d = S_START;
|
||||||
|
error_d = 1'b0;
|
||||||
|
data_d = '0;
|
||||||
|
op_read_d = 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
S_START: begin
|
||||||
|
if (tick) begin
|
||||||
|
sda_d = 1'b0;
|
||||||
|
edge_cnt_d = '0;
|
||||||
|
state_d = S_ADDR;
|
||||||
|
shift_reg_d = {slave_addr_i[7:1], op_read_q};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
S_ADDR: begin
|
||||||
|
if (tick) begin
|
||||||
|
scl_d = ~scl_q;
|
||||||
|
edge_cnt_d = edge_cnt_q + 1'b1;
|
||||||
|
// 下降沿释放SDA,准备接收ACK
|
||||||
|
if (edge_cnt_q == 5'd16) begin
|
||||||
|
sda_oe_d = 1'b0;
|
||||||
|
// 上升沿接收ACK
|
||||||
|
end else if (edge_cnt_q == 5'd17) begin
|
||||||
|
// NACK
|
||||||
|
if (sda_i) begin
|
||||||
|
error_d = 1'b1;
|
||||||
|
// ACK
|
||||||
|
end else begin
|
||||||
|
if (op_read_q) begin
|
||||||
|
state_d = S_DATA;
|
||||||
|
end else begin
|
||||||
|
state_d = S_REG;
|
||||||
|
shift_reg_d = slave_reg_i;
|
||||||
|
end
|
||||||
|
edge_cnt_d = '0;
|
||||||
|
end
|
||||||
|
// 最后一个下降沿
|
||||||
|
end else if (edge_cnt_q == 5'd18) begin
|
||||||
|
sda_d = 1'b0;
|
||||||
|
sda_oe_d = 1'b1;
|
||||||
|
// 最后一个上升沿
|
||||||
|
end else if (edge_cnt_q == 5'd19) begin
|
||||||
|
state_d = S_STOP;
|
||||||
|
end else begin
|
||||||
|
// 发数据
|
||||||
|
if (scl_q) begin
|
||||||
|
// 左移一位(MSB first)
|
||||||
|
shift_reg_d = {shift_reg_q[6:0], 1'b1};
|
||||||
|
sda_d = shift_reg_q[7];
|
||||||
|
sda_oe_d = 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
S_REG: begin
|
||||||
|
if (tick) begin
|
||||||
|
scl_d = ~scl_q;
|
||||||
|
edge_cnt_d = edge_cnt_q + 1'b1;
|
||||||
|
// 下降沿释放SDA,准备接收ACK
|
||||||
|
if (edge_cnt_q == 5'd16) begin
|
||||||
|
sda_oe_d = 1'b0;
|
||||||
|
// 上升沿接收ACK
|
||||||
|
end else if (edge_cnt_q == 5'd17) begin
|
||||||
|
// NACK
|
||||||
|
if (sda_i) begin
|
||||||
|
error_d = 1'b1;
|
||||||
|
// ACK
|
||||||
|
end else begin
|
||||||
|
// 写操作,转去S_DATA状态
|
||||||
|
if (!read_i) begin
|
||||||
|
state_d = S_DATA;
|
||||||
|
shift_reg_d = slave_data_i;
|
||||||
|
edge_cnt_d = '0;
|
||||||
|
// 读操作,发送STOP信号
|
||||||
|
end else begin
|
||||||
|
op_read_d = 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
// 最后一个下降沿
|
||||||
|
end else if (edge_cnt_q == 5'd18) begin
|
||||||
|
sda_d = 1'b0;
|
||||||
|
sda_oe_d = 1'b1;
|
||||||
|
// 最后一个上升沿
|
||||||
|
end else if (edge_cnt_q == 5'd19) begin
|
||||||
|
state_d = S_STOP;
|
||||||
|
end else begin
|
||||||
|
// 发数据
|
||||||
|
if (scl_q) begin
|
||||||
|
// 左移一位(MSB first)
|
||||||
|
shift_reg_d = {shift_reg_q[6:0], 1'b1};
|
||||||
|
sda_d = shift_reg_q[7];
|
||||||
|
sda_oe_d = 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
S_DATA: begin
|
||||||
|
if (tick) begin
|
||||||
|
scl_d = ~scl_q;
|
||||||
|
edge_cnt_d = edge_cnt_q + 1'b1;
|
||||||
|
// 下降沿释放SDA,准备接收ACK
|
||||||
|
if (edge_cnt_q == 5'd16) begin
|
||||||
|
sda_oe_d = 1'b0;
|
||||||
|
// 上升沿接收ACK
|
||||||
|
end else if (edge_cnt_q == 5'd17) begin
|
||||||
|
// NACK
|
||||||
|
if (sda_i ^ op_read_q) begin
|
||||||
|
error_d = 1'b1;
|
||||||
|
// ACK
|
||||||
|
end else begin
|
||||||
|
error_d = 1'b0;
|
||||||
|
end
|
||||||
|
op_read_d = 1'b0;
|
||||||
|
// 最后一个下降沿
|
||||||
|
end else if (edge_cnt_q == 5'd18) begin
|
||||||
|
sda_d = 1'b0;
|
||||||
|
sda_oe_d = 1'b1;
|
||||||
|
// 最后一个上升沿
|
||||||
|
end else if (edge_cnt_q == 5'd19) begin
|
||||||
|
state_d = S_STOP;
|
||||||
|
end else begin
|
||||||
|
// 读数据
|
||||||
|
if (op_read_q && (~scl_q)) begin
|
||||||
|
data_d = {data_q[6:0], sda_i};
|
||||||
|
// 发数据
|
||||||
|
end else if ((~op_read_q) && scl_q) begin
|
||||||
|
// 左移一位(MSB first)
|
||||||
|
shift_reg_d = {shift_reg_q[6:0], 1'b1};
|
||||||
|
sda_d = shift_reg_q[7];
|
||||||
|
sda_oe_d = 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
S_STOP: begin
|
||||||
|
if (tick) begin
|
||||||
|
sda_d = 1'b1;
|
||||||
|
if (op_read_q) begin
|
||||||
|
state_d = S_START;
|
||||||
|
end else begin
|
||||||
|
state_d = S_IDLE;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
default: ;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign scl_o = scl_q;
|
||||||
|
assign scl_oe_o = scl_oe_q;
|
||||||
|
assign sda_o = sda_q;
|
||||||
|
assign sda_oe_o = sda_oe_q;
|
||||||
|
assign data_o = data_q;
|
||||||
|
assign ready_o = (state_q == S_IDLE);
|
||||||
|
assign error_o = error_q;
|
||||||
|
|
||||||
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||||
|
if (!rst_ni) begin
|
||||||
|
state_q <= S_IDLE;
|
||||||
|
shift_reg_q <= '0;
|
||||||
|
scl_q <= 1'b0;
|
||||||
|
sda_q <= 1'b0;
|
||||||
|
sda_oe_q <= 1'b0;
|
||||||
|
scl_oe_q <= 1'b0;
|
||||||
|
edge_cnt_q <= '0;
|
||||||
|
data_q <= '0;
|
||||||
|
error_q <= 1'b0;
|
||||||
|
op_read_q <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
state_q <= state_d;
|
||||||
|
shift_reg_q <= shift_reg_d;
|
||||||
|
scl_q <= scl_d;
|
||||||
|
sda_q <= sda_d;
|
||||||
|
sda_oe_q <= sda_oe_d;
|
||||||
|
scl_oe_q <= scl_oe_d;
|
||||||
|
edge_cnt_q <= edge_cnt_d;
|
||||||
|
data_q <= data_d;
|
||||||
|
error_q <= error_d;
|
||||||
|
op_read_q <= op_read_d;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
logic [15:0] ratio = {1'b0, div_ratio_i[15:1]};
|
||||||
|
|
||||||
|
clk_div #(
|
||||||
|
.RATIO_WIDTH(16)
|
||||||
|
) u_clk_div (
|
||||||
|
.clk_i(clk_i),
|
||||||
|
.rst_ni(rst_ni || (~((state_q == S_IDLE) && start_i))),
|
||||||
|
.en_i(state_q != S_IDLE),
|
||||||
|
.ratio_i(ratio),
|
||||||
|
.clk_o(tick)
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
|
@ -0,0 +1,159 @@
|
||||||
|
// Copyright lowRISC contributors.
|
||||||
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// Register Package auto-generated by `reggen` containing data structure
|
||||||
|
|
||||||
|
package i2c_reg_pkg;
|
||||||
|
|
||||||
|
// Address widths within the block
|
||||||
|
parameter int BlockAw = 4;
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// Typedefs for registers //
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
struct packed {
|
||||||
|
logic q;
|
||||||
|
logic qe;
|
||||||
|
} start;
|
||||||
|
struct packed {
|
||||||
|
logic q;
|
||||||
|
logic qe;
|
||||||
|
} int_en;
|
||||||
|
struct packed {
|
||||||
|
logic q;
|
||||||
|
logic qe;
|
||||||
|
} int_pending;
|
||||||
|
struct packed {
|
||||||
|
logic q;
|
||||||
|
logic qe;
|
||||||
|
} mode;
|
||||||
|
struct packed {
|
||||||
|
logic q;
|
||||||
|
logic qe;
|
||||||
|
} write;
|
||||||
|
struct packed {
|
||||||
|
logic q;
|
||||||
|
logic qe;
|
||||||
|
} ack;
|
||||||
|
struct packed {
|
||||||
|
logic q;
|
||||||
|
logic qe;
|
||||||
|
} error;
|
||||||
|
struct packed {
|
||||||
|
logic [15:0] q;
|
||||||
|
logic qe;
|
||||||
|
} clk_div;
|
||||||
|
} i2c_reg2hw_ctrl_reg_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
struct packed {
|
||||||
|
logic [7:0] q;
|
||||||
|
} address;
|
||||||
|
struct packed {
|
||||||
|
logic [7:0] q;
|
||||||
|
} regreg;
|
||||||
|
struct packed {
|
||||||
|
logic [7:0] q;
|
||||||
|
} data;
|
||||||
|
} i2c_reg2hw_master_data_reg_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
logic [7:0] q;
|
||||||
|
logic re;
|
||||||
|
} i2c_reg2hw_slave_data_reg_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
struct packed {
|
||||||
|
logic d;
|
||||||
|
logic de;
|
||||||
|
} start;
|
||||||
|
struct packed {
|
||||||
|
logic d;
|
||||||
|
logic de;
|
||||||
|
} int_en;
|
||||||
|
struct packed {
|
||||||
|
logic d;
|
||||||
|
logic de;
|
||||||
|
} int_pending;
|
||||||
|
struct packed {
|
||||||
|
logic d;
|
||||||
|
logic de;
|
||||||
|
} mode;
|
||||||
|
struct packed {
|
||||||
|
logic d;
|
||||||
|
logic de;
|
||||||
|
} write;
|
||||||
|
struct packed {
|
||||||
|
logic d;
|
||||||
|
logic de;
|
||||||
|
} ack;
|
||||||
|
struct packed {
|
||||||
|
logic d;
|
||||||
|
logic de;
|
||||||
|
} error;
|
||||||
|
struct packed {
|
||||||
|
logic [15:0] d;
|
||||||
|
logic de;
|
||||||
|
} clk_div;
|
||||||
|
} i2c_hw2reg_ctrl_reg_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
struct packed {
|
||||||
|
logic [7:0] d;
|
||||||
|
logic de;
|
||||||
|
} address;
|
||||||
|
struct packed {
|
||||||
|
logic [7:0] d;
|
||||||
|
logic de;
|
||||||
|
} regreg;
|
||||||
|
struct packed {
|
||||||
|
logic [7:0] d;
|
||||||
|
logic de;
|
||||||
|
} data;
|
||||||
|
} i2c_hw2reg_master_data_reg_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
logic [7:0] d;
|
||||||
|
} i2c_hw2reg_slave_data_reg_t;
|
||||||
|
|
||||||
|
// Register -> HW type
|
||||||
|
typedef struct packed {
|
||||||
|
i2c_reg2hw_ctrl_reg_t ctrl; // [63:33]
|
||||||
|
i2c_reg2hw_master_data_reg_t master_data; // [32:9]
|
||||||
|
i2c_reg2hw_slave_data_reg_t slave_data; // [8:0]
|
||||||
|
} i2c_reg2hw_t;
|
||||||
|
|
||||||
|
// HW -> register type
|
||||||
|
typedef struct packed {
|
||||||
|
i2c_hw2reg_ctrl_reg_t ctrl; // [65:35]
|
||||||
|
i2c_hw2reg_master_data_reg_t master_data; // [34:8]
|
||||||
|
i2c_hw2reg_slave_data_reg_t slave_data; // [7:0]
|
||||||
|
} i2c_hw2reg_t;
|
||||||
|
|
||||||
|
// Register offsets
|
||||||
|
parameter logic [BlockAw-1:0] I2C_CTRL_OFFSET = 4'h0;
|
||||||
|
parameter logic [BlockAw-1:0] I2C_MASTER_DATA_OFFSET = 4'h4;
|
||||||
|
parameter logic [BlockAw-1:0] I2C_SLAVE_DATA_OFFSET = 4'h8;
|
||||||
|
|
||||||
|
// Reset values for hwext registers and their fields
|
||||||
|
parameter logic [7:0] I2C_SLAVE_DATA_RESVAL = 8'h0;
|
||||||
|
|
||||||
|
// Register index
|
||||||
|
typedef enum int {
|
||||||
|
I2C_CTRL,
|
||||||
|
I2C_MASTER_DATA,
|
||||||
|
I2C_SLAVE_DATA
|
||||||
|
} i2c_id_e;
|
||||||
|
|
||||||
|
// Register width information to check illegal writes
|
||||||
|
parameter logic [3:0] I2C_PERMIT [3] = '{
|
||||||
|
4'b1111, // index[0] I2C_CTRL
|
||||||
|
4'b0111, // index[1] I2C_MASTER_DATA
|
||||||
|
4'b0001 // index[2] I2C_SLAVE_DATA
|
||||||
|
};
|
||||||
|
|
||||||
|
endpackage
|
||||||
|
|
|
@ -0,0 +1,453 @@
|
||||||
|
// Copyright lowRISC contributors.
|
||||||
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// Register Top module auto-generated by `reggen`
|
||||||
|
|
||||||
|
|
||||||
|
module i2c_reg_top (
|
||||||
|
input logic clk_i,
|
||||||
|
input logic rst_ni,
|
||||||
|
|
||||||
|
// To HW
|
||||||
|
output i2c_reg_pkg::i2c_reg2hw_t reg2hw, // Write
|
||||||
|
input i2c_reg_pkg::i2c_hw2reg_t hw2reg, // Read
|
||||||
|
|
||||||
|
input logic reg_we,
|
||||||
|
input logic reg_re,
|
||||||
|
input logic [31:0] reg_wdata,
|
||||||
|
input logic [ 3:0] reg_be,
|
||||||
|
input logic [31:0] reg_addr,
|
||||||
|
output logic [31:0] reg_rdata
|
||||||
|
);
|
||||||
|
|
||||||
|
import i2c_reg_pkg::* ;
|
||||||
|
|
||||||
|
localparam int AW = 4;
|
||||||
|
localparam int DW = 32;
|
||||||
|
localparam int DBW = DW/8; // Byte Width
|
||||||
|
|
||||||
|
logic reg_error;
|
||||||
|
logic addrmiss, wr_err;
|
||||||
|
|
||||||
|
logic [DW-1:0] reg_rdata_next;
|
||||||
|
|
||||||
|
assign reg_rdata = reg_rdata_next;
|
||||||
|
assign reg_error = wr_err;
|
||||||
|
|
||||||
|
// Define SW related signals
|
||||||
|
// Format: <reg>_<field>_{wd|we|qs}
|
||||||
|
// or <reg>_{wd|we|qs} if field == 1 or 0
|
||||||
|
logic ctrl_we;
|
||||||
|
logic ctrl_start_qs;
|
||||||
|
logic ctrl_start_wd;
|
||||||
|
logic ctrl_int_en_qs;
|
||||||
|
logic ctrl_int_en_wd;
|
||||||
|
logic ctrl_int_pending_qs;
|
||||||
|
logic ctrl_int_pending_wd;
|
||||||
|
logic ctrl_mode_qs;
|
||||||
|
logic ctrl_mode_wd;
|
||||||
|
logic ctrl_write_qs;
|
||||||
|
logic ctrl_write_wd;
|
||||||
|
logic ctrl_ack_qs;
|
||||||
|
logic ctrl_error_qs;
|
||||||
|
logic [15:0] ctrl_clk_div_qs;
|
||||||
|
logic [15:0] ctrl_clk_div_wd;
|
||||||
|
logic master_data_we;
|
||||||
|
logic [7:0] master_data_address_qs;
|
||||||
|
logic [7:0] master_data_address_wd;
|
||||||
|
logic [7:0] master_data_regreg_qs;
|
||||||
|
logic [7:0] master_data_regreg_wd;
|
||||||
|
logic [7:0] master_data_data_qs;
|
||||||
|
logic [7:0] master_data_data_wd;
|
||||||
|
logic slave_data_re;
|
||||||
|
logic [7:0] slave_data_qs;
|
||||||
|
|
||||||
|
// Register instances
|
||||||
|
// R[ctrl]: V(False)
|
||||||
|
|
||||||
|
// F[start]: 0:0
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (1),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (1'h0)
|
||||||
|
) u_ctrl_start (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (ctrl_we),
|
||||||
|
.wd (ctrl_start_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.start.de),
|
||||||
|
.d (hw2reg.ctrl.start.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.start.qe),
|
||||||
|
.q (reg2hw.ctrl.start.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_start_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[int_en]: 1:1
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (1),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (1'h0)
|
||||||
|
) u_ctrl_int_en (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (ctrl_we),
|
||||||
|
.wd (ctrl_int_en_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.int_en.de),
|
||||||
|
.d (hw2reg.ctrl.int_en.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.int_en.qe),
|
||||||
|
.q (reg2hw.ctrl.int_en.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_int_en_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[int_pending]: 2:2
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (1),
|
||||||
|
.SWACCESS("W1C"),
|
||||||
|
.RESVAL (1'h0)
|
||||||
|
) u_ctrl_int_pending (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (ctrl_we),
|
||||||
|
.wd (ctrl_int_pending_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.int_pending.de),
|
||||||
|
.d (hw2reg.ctrl.int_pending.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.int_pending.qe),
|
||||||
|
.q (reg2hw.ctrl.int_pending.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_int_pending_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[mode]: 3:3
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (1),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (1'h0)
|
||||||
|
) u_ctrl_mode (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (ctrl_we),
|
||||||
|
.wd (ctrl_mode_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.mode.de),
|
||||||
|
.d (hw2reg.ctrl.mode.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.mode.qe),
|
||||||
|
.q (reg2hw.ctrl.mode.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_mode_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[write]: 4:4
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (1),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (1'h0)
|
||||||
|
) u_ctrl_write (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (ctrl_we),
|
||||||
|
.wd (ctrl_write_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.write.de),
|
||||||
|
.d (hw2reg.ctrl.write.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.write.qe),
|
||||||
|
.q (reg2hw.ctrl.write.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_write_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[ack]: 5:5
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (1),
|
||||||
|
.SWACCESS("RO"),
|
||||||
|
.RESVAL (1'h0)
|
||||||
|
) u_ctrl_ack (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (1'b0),
|
||||||
|
.wd ('0),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.ack.de),
|
||||||
|
.d (hw2reg.ctrl.ack.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.ack.qe),
|
||||||
|
.q (reg2hw.ctrl.ack.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_ack_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[error]: 6:6
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (1),
|
||||||
|
.SWACCESS("RO"),
|
||||||
|
.RESVAL (1'h0)
|
||||||
|
) u_ctrl_error (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (1'b0),
|
||||||
|
.wd ('0),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.error.de),
|
||||||
|
.d (hw2reg.ctrl.error.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.error.qe),
|
||||||
|
.q (reg2hw.ctrl.error.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_error_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[clk_div]: 31:16
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (16),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (16'h0)
|
||||||
|
) u_ctrl_clk_div (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (ctrl_we),
|
||||||
|
.wd (ctrl_clk_div_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.ctrl.clk_div.de),
|
||||||
|
.d (hw2reg.ctrl.clk_div.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (reg2hw.ctrl.clk_div.qe),
|
||||||
|
.q (reg2hw.ctrl.clk_div.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (ctrl_clk_div_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// R[master_data]: V(False)
|
||||||
|
|
||||||
|
// F[address]: 7:0
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (8),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (8'h0)
|
||||||
|
) u_master_data_address (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (master_data_we),
|
||||||
|
.wd (master_data_address_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.master_data.address.de),
|
||||||
|
.d (hw2reg.master_data.address.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (),
|
||||||
|
.q (reg2hw.master_data.address.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (master_data_address_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[regreg]: 15:8
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (8),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (8'h0)
|
||||||
|
) u_master_data_regreg (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (master_data_we),
|
||||||
|
.wd (master_data_regreg_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.master_data.regreg.de),
|
||||||
|
.d (hw2reg.master_data.regreg.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (),
|
||||||
|
.q (reg2hw.master_data.regreg.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (master_data_regreg_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// F[data]: 23:16
|
||||||
|
prim_subreg #(
|
||||||
|
.DW (8),
|
||||||
|
.SWACCESS("RW"),
|
||||||
|
.RESVAL (8'h0)
|
||||||
|
) u_master_data_data (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
|
||||||
|
// from register interface
|
||||||
|
.we (master_data_we),
|
||||||
|
.wd (master_data_data_wd),
|
||||||
|
|
||||||
|
// from internal hardware
|
||||||
|
.de (hw2reg.master_data.data.de),
|
||||||
|
.d (hw2reg.master_data.data.d),
|
||||||
|
|
||||||
|
// to internal hardware
|
||||||
|
.qe (),
|
||||||
|
.q (reg2hw.master_data.data.q),
|
||||||
|
|
||||||
|
// to register interface (read)
|
||||||
|
.qs (master_data_data_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// R[slave_data]: V(True)
|
||||||
|
|
||||||
|
prim_subreg_ext #(
|
||||||
|
.DW (8)
|
||||||
|
) u_slave_data (
|
||||||
|
.re (slave_data_re),
|
||||||
|
.we (1'b0),
|
||||||
|
.wd ('0),
|
||||||
|
.d (hw2reg.slave_data.d),
|
||||||
|
.qre (reg2hw.slave_data.re),
|
||||||
|
.qe (),
|
||||||
|
.q (reg2hw.slave_data.q),
|
||||||
|
.qs (slave_data_qs)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
logic [2:0] addr_hit;
|
||||||
|
always_comb begin
|
||||||
|
addr_hit = '0;
|
||||||
|
addr_hit[0] = (reg_addr == I2C_CTRL_OFFSET);
|
||||||
|
addr_hit[1] = (reg_addr == I2C_MASTER_DATA_OFFSET);
|
||||||
|
addr_hit[2] = (reg_addr == I2C_SLAVE_DATA_OFFSET);
|
||||||
|
end
|
||||||
|
|
||||||
|
assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ;
|
||||||
|
|
||||||
|
// Check sub-word write is permitted
|
||||||
|
always_comb begin
|
||||||
|
wr_err = (reg_we &
|
||||||
|
((addr_hit[0] & (|(I2C_PERMIT[0] & ~reg_be))) |
|
||||||
|
(addr_hit[1] & (|(I2C_PERMIT[1] & ~reg_be))) |
|
||||||
|
(addr_hit[2] & (|(I2C_PERMIT[2] & ~reg_be)))));
|
||||||
|
end
|
||||||
|
|
||||||
|
assign ctrl_we = addr_hit[0] & reg_we & !reg_error;
|
||||||
|
|
||||||
|
assign ctrl_start_wd = reg_wdata[0];
|
||||||
|
|
||||||
|
assign ctrl_int_en_wd = reg_wdata[1];
|
||||||
|
|
||||||
|
assign ctrl_int_pending_wd = reg_wdata[2];
|
||||||
|
|
||||||
|
assign ctrl_mode_wd = reg_wdata[3];
|
||||||
|
|
||||||
|
assign ctrl_write_wd = reg_wdata[4];
|
||||||
|
|
||||||
|
assign ctrl_clk_div_wd = reg_wdata[31:16];
|
||||||
|
assign master_data_we = addr_hit[1] & reg_we & !reg_error;
|
||||||
|
|
||||||
|
assign master_data_address_wd = reg_wdata[7:0];
|
||||||
|
|
||||||
|
assign master_data_regreg_wd = reg_wdata[15:8];
|
||||||
|
|
||||||
|
assign master_data_data_wd = reg_wdata[23:16];
|
||||||
|
assign slave_data_re = addr_hit[2] & reg_re & !reg_error;
|
||||||
|
|
||||||
|
// Read data return
|
||||||
|
always_comb begin
|
||||||
|
reg_rdata_next = '0;
|
||||||
|
unique case (1'b1)
|
||||||
|
addr_hit[0]: begin
|
||||||
|
reg_rdata_next[0] = ctrl_start_qs;
|
||||||
|
reg_rdata_next[1] = ctrl_int_en_qs;
|
||||||
|
reg_rdata_next[2] = ctrl_int_pending_qs;
|
||||||
|
reg_rdata_next[3] = ctrl_mode_qs;
|
||||||
|
reg_rdata_next[4] = ctrl_write_qs;
|
||||||
|
reg_rdata_next[5] = ctrl_ack_qs;
|
||||||
|
reg_rdata_next[6] = ctrl_error_qs;
|
||||||
|
reg_rdata_next[31:16] = ctrl_clk_div_qs;
|
||||||
|
end
|
||||||
|
|
||||||
|
addr_hit[1]: begin
|
||||||
|
reg_rdata_next[7:0] = master_data_address_qs;
|
||||||
|
reg_rdata_next[15:8] = master_data_regreg_qs;
|
||||||
|
reg_rdata_next[23:16] = master_data_data_qs;
|
||||||
|
end
|
||||||
|
|
||||||
|
addr_hit[2]: begin
|
||||||
|
reg_rdata_next[7:0] = slave_data_qs;
|
||||||
|
end
|
||||||
|
|
||||||
|
default: begin
|
||||||
|
reg_rdata_next = '1;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
// Unused signal tieoff
|
||||||
|
|
||||||
|
// wdata / byte enable are not always fully used
|
||||||
|
// add a blanket unused statement to handle lint waivers
|
||||||
|
logic unused_wdata;
|
||||||
|
logic unused_be;
|
||||||
|
assign unused_wdata = ^reg_wdata;
|
||||||
|
assign unused_be = ^reg_be;
|
||||||
|
|
||||||
|
endmodule
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 Blue Liang, liangkangnan@163.com
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module i2c_top (
|
||||||
|
input logic clk_i,
|
||||||
|
input logic rst_ni,
|
||||||
|
|
||||||
|
output logic scl_o,
|
||||||
|
output logic scl_oe_o,
|
||||||
|
input logic scl_i,
|
||||||
|
output logic sda_o,
|
||||||
|
output logic sda_oe_o,
|
||||||
|
input logic sda_i,
|
||||||
|
|
||||||
|
output logic irq_o,
|
||||||
|
|
||||||
|
// OBI总线接口信号
|
||||||
|
input logic req_i,
|
||||||
|
input logic we_i,
|
||||||
|
input logic [ 3:0] be_i,
|
||||||
|
input logic [31:0] addr_i,
|
||||||
|
input logic [31:0] data_i,
|
||||||
|
output logic [31:0] data_o
|
||||||
|
);
|
||||||
|
|
||||||
|
logic re;
|
||||||
|
logic we;
|
||||||
|
logic [31:0] addr;
|
||||||
|
logic [31:0] reg_rdata;
|
||||||
|
|
||||||
|
// 读信号
|
||||||
|
assign re = req_i & (!we_i);
|
||||||
|
// 写信号
|
||||||
|
assign we = req_i & we_i;
|
||||||
|
// 去掉基地址
|
||||||
|
assign addr = {16'h0, addr_i[15:0]};
|
||||||
|
|
||||||
|
i2c_core u_i2c_core (
|
||||||
|
.clk_i (clk_i),
|
||||||
|
.rst_ni (rst_ni),
|
||||||
|
.scl_o (scl_o),
|
||||||
|
.scl_oe_o (scl_oe_o),
|
||||||
|
.scl_i (scl_i),
|
||||||
|
.sda_o (sda_o),
|
||||||
|
.sda_oe_o (sda_oe_o),
|
||||||
|
.sda_i (sda_i),
|
||||||
|
.irq_o (irq_o),
|
||||||
|
.reg_we_i (we),
|
||||||
|
.reg_re_i (re),
|
||||||
|
.reg_wdata_i(data_i),
|
||||||
|
.reg_be_i (be_i),
|
||||||
|
.reg_addr_i (addr),
|
||||||
|
.reg_rdata_o(reg_rdata)
|
||||||
|
);
|
||||||
|
|
||||||
|
always_ff @(posedge clk_i) begin
|
||||||
|
data_o <= reg_rdata;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
|
@ -31,6 +31,9 @@ module tinyriscv_soc_top #(
|
||||||
output wire uart_tx_pin, // UART发送引脚
|
output wire uart_tx_pin, // UART发送引脚
|
||||||
input wire uart_rx_pin, // UART接收引脚
|
input wire uart_rx_pin, // UART接收引脚
|
||||||
|
|
||||||
|
inout wire i2c_scl_pin, // I2C SCL引脚
|
||||||
|
inout wire i2c_sda_pin, // I2C SDA引脚
|
||||||
|
|
||||||
inout wire[1:0] gpio_pins, // GPIO引脚,1bit代表一个GPIO
|
inout wire[1:0] gpio_pins, // GPIO引脚,1bit代表一个GPIO
|
||||||
|
|
||||||
input wire jtag_TCK_pin, // JTAG TCK引脚
|
input wire jtag_TCK_pin, // JTAG TCK引脚
|
||||||
|
@ -42,9 +45,9 @@ module tinyriscv_soc_top #(
|
||||||
|
|
||||||
localparam int MASTERS = 3; // Number of master ports
|
localparam int MASTERS = 3; // Number of master ports
|
||||||
`ifdef VERILATOR
|
`ifdef VERILATOR
|
||||||
localparam int SLAVES = 8; // Number of slave ports
|
localparam int SLAVES = 9; // Number of slave ports
|
||||||
`else
|
`else
|
||||||
localparam int SLAVES = 7; // Number of slave ports
|
localparam int SLAVES = 8; // Number of slave ports
|
||||||
`endif
|
`endif
|
||||||
|
|
||||||
// masters
|
// masters
|
||||||
|
@ -60,8 +63,9 @@ module tinyriscv_soc_top #(
|
||||||
localparam int Gpio = 4;
|
localparam int Gpio = 4;
|
||||||
localparam int Uart0 = 5;
|
localparam int Uart0 = 5;
|
||||||
localparam int Rvic = 6;
|
localparam int Rvic = 6;
|
||||||
|
localparam int I2c = 7;
|
||||||
`ifdef VERILATOR
|
`ifdef VERILATOR
|
||||||
localparam int SimCtrl = 7;
|
localparam int SimCtrl = 8;
|
||||||
`endif
|
`endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -110,17 +114,26 @@ module tinyriscv_soc_top #(
|
||||||
wire uart0_irq;
|
wire uart0_irq;
|
||||||
wire gpio0_irq;
|
wire gpio0_irq;
|
||||||
wire gpio1_irq;
|
wire gpio1_irq;
|
||||||
|
wire i2c0_irq;
|
||||||
|
|
||||||
wire[GPIO_NUM-1:0] gpio_data_in;
|
wire[GPIO_NUM-1:0] gpio_data_in;
|
||||||
wire[GPIO_NUM-1:0] gpio_oe;
|
wire[GPIO_NUM-1:0] gpio_oe;
|
||||||
wire[GPIO_NUM-1:0] gpio_data_out;
|
wire[GPIO_NUM-1:0] gpio_data_out;
|
||||||
|
|
||||||
|
wire i2c_scl_in;
|
||||||
|
wire i2c_scl_oe;
|
||||||
|
wire i2c_scl_out;
|
||||||
|
wire i2c_sda_in;
|
||||||
|
wire i2c_sda_oe;
|
||||||
|
wire i2c_sda_out;
|
||||||
|
|
||||||
always @ (*) begin
|
always @ (*) begin
|
||||||
irq_src = 32'h0;
|
irq_src = 32'h0;
|
||||||
irq_src[0] = timer0_irq;
|
irq_src[0] = timer0_irq;
|
||||||
irq_src[1] = uart0_irq;
|
irq_src[1] = uart0_irq;
|
||||||
irq_src[2] = gpio0_irq;
|
irq_src[2] = gpio0_irq;
|
||||||
irq_src[3] = gpio1_irq;
|
irq_src[3] = gpio1_irq;
|
||||||
|
irq_src[4] = i2c0_irq;
|
||||||
end
|
end
|
||||||
|
|
||||||
`ifdef VERILATOR
|
`ifdef VERILATOR
|
||||||
|
@ -269,6 +282,32 @@ module tinyriscv_soc_top #(
|
||||||
.data_o (slave_rdata[Rvic])
|
.data_o (slave_rdata[Rvic])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assign i2c_scl_pin = i2c_scl_oe ? i2c_scl_out : 1'bz;
|
||||||
|
assign i2c_scl_in = i2c_scl_pin;
|
||||||
|
assign i2c_sda_pin = i2c_sda_oe ? i2c_sda_out : 1'bz;
|
||||||
|
assign i2c_sda_in = i2c_sda_pin;
|
||||||
|
|
||||||
|
assign slave_addr_mask[I2c] = `I2C0_ADDR_MASK;
|
||||||
|
assign slave_addr_base[I2c] = `I2C0_ADDR_BASE;
|
||||||
|
// 7.I2C0模块
|
||||||
|
i2c_top i2c0(
|
||||||
|
.clk_i (clk),
|
||||||
|
.rst_ni (ndmreset_n),
|
||||||
|
.scl_o (i2c_scl_out),
|
||||||
|
.scl_oe_o (i2c_scl_oe),
|
||||||
|
.scl_i (i2c_scl_in),
|
||||||
|
.sda_o (i2c_sda_out),
|
||||||
|
.sda_oe_o (i2c_sda_oe),
|
||||||
|
.sda_i (i2c_sda_in),
|
||||||
|
.irq_o (i2c0_irq),
|
||||||
|
.req_i (slave_req[I2c]),
|
||||||
|
.we_i (slave_we[I2c]),
|
||||||
|
.be_i (slave_be[I2c]),
|
||||||
|
.addr_i (slave_addr[I2c]),
|
||||||
|
.data_i (slave_wdata[I2c]),
|
||||||
|
.data_o (slave_rdata[I2c])
|
||||||
|
);
|
||||||
|
|
||||||
`ifdef VERILATOR
|
`ifdef VERILATOR
|
||||||
assign slave_addr_mask[SimCtrl] = `SIM_CTRL_ADDR_MASK;
|
assign slave_addr_mask[SimCtrl] = `SIM_CTRL_ADDR_MASK;
|
||||||
assign slave_addr_base[SimCtrl] = `SIM_CTRL_ADDR_BASE;
|
assign slave_addr_base[SimCtrl] = `SIM_CTRL_ADDR_BASE;
|
||||||
|
|
Loading…
Reference in New Issue