rtl:perips: add spi master

Signed-off-by: liangkangnan <liangkangnan@163.com>
pull/4/head
liangkangnan 2021-09-06 10:01:56 +08:00
parent f74f2d8f5d
commit 57690b00bd
11 changed files with 1930 additions and 4 deletions

View File

@ -30,6 +30,30 @@ set_property PACKAGE_PIN R10 [get_ports i2c_scl_pin]
set_property IOSTANDARD LVCMOS33 [get_ports i2c_sda_pin]
set_property PACKAGE_PIN R11 [get_ports i2c_sda_pin]
# SPI DQ3引脚
set_property IOSTANDARD LVCMOS33 [get_ports spi_dq3_pin]
set_property PACKAGE_PIN P3 [get_ports spi_dq3_pin]
# SPI DQ2引脚
set_property IOSTANDARD LVCMOS33 [get_ports spi_dq2_pin]
set_property PACKAGE_PIN P4 [get_ports spi_dq2_pin]
# SPI DQ1引脚
set_property IOSTANDARD LVCMOS33 [get_ports spi_dq1_pin]
set_property PACKAGE_PIN P1 [get_ports spi_dq1_pin]
# SPI DQ0引脚
set_property IOSTANDARD LVCMOS33 [get_ports spi_dq0_pin]
set_property PACKAGE_PIN N1 [get_ports spi_dq0_pin]
# SPI SS引脚
set_property IOSTANDARD LVCMOS33 [get_ports spi_ss_pin]
set_property PACKAGE_PIN M5 [get_ports spi_ss_pin]
# SPI CLK引脚
set_property IOSTANDARD LVCMOS33 [get_ports spi_clk_pin]
set_property PACKAGE_PIN N4 [get_ports spi_clk_pin]
# GPIO0引脚
set_property IOSTANDARD LVCMOS33 [get_ports {gpio_pins[0]}]
set_property PACKAGE_PIN P16 [get_ports {gpio_pins[0]}]

View File

@ -59,6 +59,12 @@
../rtl/perips/i2c/i2c_top.sv
../rtl/perips/i2c/i2c_master.sv
../rtl/perips/i2c/i2c_slave.sv
../rtl/perips/spi/spi_reg_pkg.sv
../rtl/perips/spi/spi_reg_top.sv
../rtl/perips/spi/spi_core.sv
../rtl/perips/spi/spi_top.sv
../rtl/perips/spi/spi_master.sv
../rtl/perips/spi/spi_transmit_byte.sv
../rtl/sys_bus/obi_interconnect.sv
../rtl/sys_bus/obi_interconnect_master_sel.sv

View File

@ -43,6 +43,9 @@
// I2C0
`define I2C0_ADDR_MASK ~32'hffff
`define I2C0_ADDR_BASE 32'h60000000
// SPI0
`define SPI0_ADDR_MASK ~32'hffff
`define SPI0_ADDR_BASE 32'h70000000
// Interrupt controller
`define RVIC_ADDR_MASK ~32'hffff
`define RVIC_ADDR_BASE 32'hD0000000

116
rtl/perips/spi/spi.hjson Normal file
View File

@ -0,0 +1,116 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{ name: "spi",
clocking: [{clock: "clk_i", reset: "rst_ni"}],
bus_interfaces: [
{ protocol: "tlul", direction: "device" }
],
regwidth: "32",
registers: [
{ name: "CTRL0",
desc: "SPI control 0 register",
swaccess: "rw",
hwaccess: "hrw",
hwqe: "true",
fields: [
{ bits: "0",
name: "ENABLE",
desc: "SPI enable",
}
{ bits: "1",
name: "INT_EN",
desc: "SPI interrupt enable",
}
{ bits: "2",
name: "INT_PENDING",
swaccess: "rw1c",
desc: "SPI master transmit completely interrupt pending",
}
{ bits: "3",
name: "ROLE_MODE",
desc: "SPI role mode, 0: master, 1: slave",
}
{ bits: "5:4",
name: "CP_MODE",
desc: "SPI CPOL and CPHA mode",
}
{ bits: "7:6",
name: "SPI_MODE",
desc: "0: normal, 1: dual, 2: quad",
}
{ bits: "8",
name: "READ",
desc: "0: write, 1: read",
}
{ bits: "9",
name: "MSB_FIRST",
desc: "0: lsb, 1: msb",
}
{ bits: "10",
name: "SS_SW_CTRL",
desc: "ss ctrl by software. 0: hw, 1: sw",
}
{ bits: "11",
name: "SS_LEVEL",
desc: "ss output level. valid only when bit[10]=1",
}
{ bits: "15:12",
name: "SS_DELAY",
desc: "SPI ss signal active or inactive how many spi clk",
}
{ bits: "31:29",
name: "CLK_DIV",
desc: "SPI clock divider count",
}
]
}
{ name: "STATUS",
desc: "SPI status register",
swaccess: "ro"
hwaccess: "hrw"
hwext: "true"
fields: [
{ bits: "0",
name: "TX_FIFO_FULL",
desc: "tx fifo is full",
}
{ bits: "1",
name: "TX_FIFO_EMPTY",
desc: "tx fifo is empty",
}
{ bits: "2",
name: "RX_FIFO_FULL",
desc: "rx fifo is full",
}
{ bits: "3",
name: "RX_FIFO_EMPTY",
desc: "rx fifo is empty",
}
{ bits: "4",
name: "BUSY",
desc: "SPI is transmitting or nor, 0: IDLE, 1: BUSY",
}
]
}
{ name: "TXDATA",
desc: "SPI TX data register",
swaccess: "wo",
hwaccess: "hro",
hwqe: "true",
fields: [
{ bits: "7:0" }
]
}
{ name: "RXDATA",
desc: "SPI RX data register",
swaccess: "ro",
hwaccess: "hrw",
hwext: "true",
hwre: "true",
fields: [
{ bits: "7:0" }
]
}
]
}

243
rtl/perips/spi/spi_core.sv Normal file
View File

@ -0,0 +1,243 @@
/*
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 spi_core #(
parameter int unsigned TX_FIFO_DEPTH = 8,
parameter int unsigned RX_FIFO_DEPTH = 8
)(
input logic clk_i,
input logic rst_ni,
// SPI引脚信号
input logic spi_clk_i,
output logic spi_clk_o,
output logic spi_clk_oe_o,
input logic spi_ss_i,
output logic spi_ss_o,
output logic spi_ss_oe_o,
input logic spi_dq0_i,
output logic spi_dq0_o,
output logic spi_dq0_oe_o,
input logic spi_dq1_i,
output logic spi_dq1_o,
output logic spi_dq1_oe_o,
input logic spi_dq2_i,
output logic spi_dq2_o,
output logic spi_dq2_oe_o,
input logic spi_dq3_i,
output logic spi_dq3_o,
output logic spi_dq3_oe_o,
// 中断信号
output logic irq_o,
// OBI总线接口信号
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 spi_reg_pkg::*;
parameter int unsigned TX_FIFO_ADDR_DEPTH = (TX_FIFO_DEPTH > 1) ? $clog2(TX_FIFO_DEPTH) : 1;
parameter int unsigned RX_FIFO_ADDR_DEPTH = (RX_FIFO_DEPTH > 1) ? $clog2(RX_FIFO_DEPTH) : 1;
spi_reg_pkg::spi_reg2hw_t reg2hw;
spi_reg_pkg::spi_hw2reg_t hw2reg;
logic [TX_FIFO_ADDR_DEPTH-1:0] tx_fifo_usage;
logic [RX_FIFO_ADDR_DEPTH-1:0] rx_fifo_usage;
logic master_enable;
logic master_start;
logic master_ready, master_ready_re, master_ready_fe;
logic master_read;
logic master_msb_first;
logic master_data_valid, master_data_valid_re;
logic master_ss_sw_ctrl;
logic master_ss_level;
logic busy_q;
logic [2:0] master_clk_div;
logic [1:0] master_cp_mode;
logic [1:0] master_spi_mode;
logic [3:0] master_ss_delay_cnt;
logic [7:0] master_data_out;
logic tx_fifo_full;
logic tx_fifo_empty;
logic [7:0] tx_fifo_data_in;
logic tx_fifo_push;
logic [7:0] tx_fifo_data_out;
logic tx_fifo_pop;
logic rx_fifo_full;
logic rx_fifo_empty;
logic [7:0] rx_fifo_data_in;
logic rx_fifo_push;
logic [7:0] rx_fifo_data_out;
logic rx_fifo_pop;
assign master_enable = ~reg2hw.ctrl0.role_mode.q;
assign master_start = reg2hw.ctrl0.enable.q && (!tx_fifo_empty);
assign master_read = reg2hw.ctrl0.read.q;
assign master_msb_first = reg2hw.ctrl0.msb_first.q;
assign master_clk_div = reg2hw.ctrl0.clk_div.q;
assign master_cp_mode = reg2hw.ctrl0.cp_mode.q;
assign master_spi_mode = reg2hw.ctrl0.spi_mode.q;
assign master_ss_delay_cnt = reg2hw.ctrl0.ss_delay.q;
assign master_ss_sw_ctrl = reg2hw.ctrl0.ss_sw_ctrl.q;
assign master_ss_level = reg2hw.ctrl0.ss_level.q;
assign tx_fifo_push = reg2hw.txdata.qe;
assign tx_fifo_data_in = reg2hw.txdata.q;
assign tx_fifo_pop = master_data_valid_re | master_ready_fe;
// 读操作才把接收到的数据压入RX FIFO
assign rx_fifo_push = master_data_valid_re & master_read;
assign rx_fifo_data_in = master_data_out;
assign rx_fifo_pop = reg2hw.rxdata.re;
assign hw2reg.rxdata.d = rx_fifo_data_out;
assign hw2reg.status.tx_fifo_full.d = tx_fifo_full;
assign hw2reg.status.tx_fifo_empty.d = tx_fifo_empty;
assign hw2reg.status.rx_fifo_full.d = rx_fifo_full;
assign hw2reg.status.rx_fifo_empty.d = rx_fifo_empty;
// 传输完成置位中断pending
assign hw2reg.ctrl0.int_pending.d = 1'b1;
assign hw2reg.ctrl0.int_pending.de = master_enable & master_ready_re & reg2hw.ctrl0.int_en.q;
// 传输完成清零busy位
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
busy_q <= 1'b0;
end else begin
if (master_start) begin
busy_q <= 1'b1;
end else if (master_enable & master_ready_re) begin
busy_q <= 1'b0;
end
end
end
assign hw2reg.status.busy.d = busy_q;
// 中断信号
assign irq_o = reg2hw.ctrl0.int_pending.q;
edge_detect #(
.DP(0)
) master_data_valid_ed (
.clk_i(clk_i),
.rst_ni(rst_ni),
.sig_i(master_data_valid),
.sig_o(),
.re_o(master_data_valid_re),
.fe_o()
);
edge_detect #(
.DP(0)
) master_ready_ed (
.clk_i(clk_i),
.rst_ni(rst_ni),
.sig_i(master_ready),
.sig_o(),
.re_o(master_ready_re),
.fe_o(master_ready_fe)
);
// TX FIFO
sync_fifo #(
.DATA_WIDTH(8),
.DEPTH(TX_FIFO_DEPTH)
) u_tx_fifo (
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i (1'b0),
.full_o (tx_fifo_full),
.empty_o (tx_fifo_empty),
.usage_o (tx_fifo_usage),
.data_i (tx_fifo_data_in),
.push_i (tx_fifo_push),
.data_o (tx_fifo_data_out),
.pop_i (tx_fifo_pop)
);
// RX FIFO
sync_fifo #(
.DATA_WIDTH(8),
.DEPTH(RX_FIFO_DEPTH)
) u_rx_fifo (
.clk_i (clk_i),
.rst_ni (rst_ni),
.flush_i (1'b0),
.testmode_i (1'b0),
.full_o (rx_fifo_full),
.empty_o (rx_fifo_empty),
.usage_o (rx_fifo_usage),
.data_i (rx_fifo_data_in),
.push_i (rx_fifo_push),
.data_o (rx_fifo_data_out),
.pop_i (rx_fifo_pop)
);
spi_master u_spi_master (
.clk_i (clk_i),
.rst_ni (rst_ni),
.start_i (master_start),
.read_i (master_read),
.data_i (tx_fifo_data_out),
.spi_mode_i (master_spi_mode),
.cp_mode_i (master_cp_mode),
.div_ratio_i (master_clk_div),
.msb_first_i (master_msb_first),
.ss_delay_cnt_i(master_ss_delay_cnt),
.ss_sw_ctrl_i (master_ss_sw_ctrl),
.ss_level_i (master_ss_level),
.data_o (master_data_out),
.ready_o (master_ready),
.data_valid_o (master_data_valid),
.spi_clk_o (spi_clk_o),
.spi_clk_oe_o (spi_clk_oe_o),
.spi_ss_o (spi_ss_o),
.spi_ss_oe_o (spi_ss_oe_o),
.spi_dq0_i (spi_dq0_i),
.spi_dq0_o (spi_dq0_o),
.spi_dq0_oe_o (spi_dq0_oe_o),
.spi_dq1_i (spi_dq1_i),
.spi_dq1_o (spi_dq1_o),
.spi_dq1_oe_o (spi_dq1_oe_o),
.spi_dq2_i (spi_dq2_i),
.spi_dq2_o (spi_dq2_o),
.spi_dq2_oe_o (spi_dq2_oe_o),
.spi_dq3_i (spi_dq3_i),
.spi_dq3_o (spi_dq3_o),
.spi_dq3_oe_o (spi_dq3_oe_o)
);
spi_reg_top u_spi_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

View File

@ -0,0 +1,195 @@
/*
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 spi_master (
input logic clk_i,
input logic rst_ni,
input logic start_i, // 开始传输
input logic read_i, // 0: write, 1: read
input logic [7:0] data_i, // 字节输入
input logic [1:0] spi_mode_i, // 0: SPI, 1: Dual SPI, 2: Quad SPI, 3: SPI
input logic [1:0] cp_mode_i, // [1]表示CPOL, [0]表示CPHA
input logic [2:0] div_ratio_i, // 分频比
input logic msb_first_i, // 1: MSB, 0: LSB
input logic [3:0] ss_delay_cnt_i, // SS信号延时时钟个数
input logic ss_sw_ctrl_i, // 软件控制SS信号
input logic ss_level_i, // SS输出电平仅当ss_sw_ctrl_i=1时有效
output logic [7:0] data_o, // 接收到的数据
output logic ready_o, // 1: IDLE, 0: 正在传输
output logic data_valid_o, // 接收到的数据有效
// CLK
output logic spi_clk_o,
output logic spi_clk_oe_o,
// SS
output logic spi_ss_o,
output logic spi_ss_oe_o,
// MOSI(DQ0)
input logic spi_dq0_i,
output logic spi_dq0_o,
output logic spi_dq0_oe_o,
// MISO(DQ1)
input logic spi_dq1_i,
output logic spi_dq1_o,
output logic spi_dq1_oe_o,
// DQ2
input logic spi_dq2_i,
output logic spi_dq2_o,
output logic spi_dq2_oe_o,
// DQ3
input logic spi_dq3_i,
output logic spi_dq3_o,
output logic spi_dq3_oe_o
);
localparam S_IDLE = 4'b0001;
localparam S_SS_ACTIVE = 4'b0010;
localparam S_TRANSMIT = 4'b0100;
localparam S_SS_INACTIVE = 4'b1000;
logic data_valid;
logic [7:0] in_data;
logic [3:0] state_d, state_q;
logic [3:0] ss_delay_cnt_d, ss_delay_cnt_q;
logic [7:0] out_data_d, out_data_q;
logic start_d, start_q;
logic ready_d, ready_q;
logic spi_ss_d, spi_ss_q;
logic spi_ss_oe_d, spi_ss_oe_q;
always_comb begin
state_d = state_q;
ss_delay_cnt_d = ss_delay_cnt_q;
out_data_d = out_data_q;
start_d = 1'b0;
ready_d = ready_q;
spi_ss_d = spi_ss_q;
spi_ss_oe_d = spi_ss_oe_q;
case (state_q)
S_IDLE: begin
spi_ss_oe_d = 1'b1;
spi_ss_d = 1'b1;
ready_d = 1'b1;
if (start_i) begin
out_data_d = data_i;
state_d = S_SS_ACTIVE;
ss_delay_cnt_d = '0;
ready_d = 1'b0;
end
end
S_SS_ACTIVE: begin
spi_ss_d = 1'b0;
ss_delay_cnt_d = ss_delay_cnt_q + 1'b1;
if (ss_delay_cnt_q == ss_delay_cnt_i) begin
state_d = S_TRANSMIT;
start_d = 1'b1;
end
end
S_TRANSMIT: begin
// 还有数据要传输
if (data_valid && start_i) begin
out_data_d = data_i;
start_d = 1'b1;
// 没有数据要传输
end else if (data_valid && (!start_i)) begin
state_d = S_SS_INACTIVE;
ss_delay_cnt_d = '0;
end
end
S_SS_INACTIVE: begin
ss_delay_cnt_d = ss_delay_cnt_q + 1'b1;
if (ss_delay_cnt_q == ss_delay_cnt_i) begin
state_d = S_IDLE;
end
end
default: ;
endcase
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
state_q <= S_IDLE;
ss_delay_cnt_q <= '0;
out_data_q <= '0;
start_q <= '0;
ready_q <= '0;
end else begin
state_q <= state_d;
ss_delay_cnt_q <= ss_delay_cnt_d;
out_data_q <= out_data_d;
start_q <= start_d;
ready_q <= ready_d;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
spi_ss_q <= '0;
spi_ss_oe_q <= '0;
end else begin
spi_ss_q <= spi_ss_d;
spi_ss_oe_q <= spi_ss_oe_d;
end
end
assign data_valid_o = data_valid;
assign data_o = in_data;
assign ready_o = ready_q;
assign spi_ss_o = ss_sw_ctrl_i ? ss_level_i : spi_ss_q;
assign spi_ss_oe_o = spi_ss_oe_q;
spi_transmit_byte master_transmit_byte (
.clk_i (clk_i),
.rst_ni (rst_ni),
.start_i (start_q),
.slave_mode_i(1'b0),
.read_i (read_i),
.spi_mode_i (spi_mode_i),
.cp_mode_i (cp_mode_i),
.data_i (out_data_q),
.div_ratio_i(div_ratio_i),
.msb_first_i(msb_first_i),
.data_o (in_data),
.ready_o (),
.data_valid_o(data_valid),
.spi_clk_i (),
.spi_clk_o (spi_clk_o),
.spi_clk_oe_o(spi_clk_oe_o),
.spi_ss_i (),
.spi_dq0_i (spi_dq0_i),
.spi_dq0_o (spi_dq0_o),
.spi_dq0_oe_o(spi_dq0_oe_o),
.spi_dq1_i (spi_dq1_i),
.spi_dq1_o (spi_dq1_o),
.spi_dq1_oe_o(spi_dq1_oe_o),
.spi_dq2_i (spi_dq2_i),
.spi_dq2_o (spi_dq2_o),
.spi_dq2_oe_o(spi_dq2_oe_o),
.spi_dq3_i (spi_dq3_i),
.spi_dq3_o (spi_dq3_o),
.spi_dq3_oe_o(spi_dq3_oe_o)
);
endmodule

View File

@ -0,0 +1,210 @@
// 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 spi_reg_pkg;
// Address widths within the block
parameter int BlockAw = 4;
////////////////////////////
// Typedefs for registers //
////////////////////////////
typedef struct packed {
struct packed {
logic q;
logic qe;
} enable;
struct packed {
logic q;
logic qe;
} int_en;
struct packed {
logic q;
logic qe;
} int_pending;
struct packed {
logic q;
logic qe;
} role_mode;
struct packed {
logic [1:0] q;
logic qe;
} cp_mode;
struct packed {
logic [1:0] q;
logic qe;
} spi_mode;
struct packed {
logic q;
logic qe;
} read;
struct packed {
logic q;
logic qe;
} msb_first;
struct packed {
logic q;
logic qe;
} ss_sw_ctrl;
struct packed {
logic q;
logic qe;
} ss_level;
struct packed {
logic [3:0] q;
logic qe;
} ss_delay;
struct packed {
logic [2:0] q;
logic qe;
} clk_div;
} spi_reg2hw_ctrl0_reg_t;
typedef struct packed {
struct packed {
logic q;
} tx_fifo_full;
struct packed {
logic q;
} tx_fifo_empty;
struct packed {
logic q;
} rx_fifo_full;
struct packed {
logic q;
} rx_fifo_empty;
struct packed {
logic q;
} busy;
} spi_reg2hw_status_reg_t;
typedef struct packed {
logic [7:0] q;
logic qe;
} spi_reg2hw_txdata_reg_t;
typedef struct packed {
logic [7:0] q;
logic re;
} spi_reg2hw_rxdata_reg_t;
typedef struct packed {
struct packed {
logic d;
logic de;
} enable;
struct packed {
logic d;
logic de;
} int_en;
struct packed {
logic d;
logic de;
} int_pending;
struct packed {
logic d;
logic de;
} role_mode;
struct packed {
logic [1:0] d;
logic de;
} cp_mode;
struct packed {
logic [1:0] d;
logic de;
} spi_mode;
struct packed {
logic d;
logic de;
} read;
struct packed {
logic d;
logic de;
} msb_first;
struct packed {
logic d;
logic de;
} ss_sw_ctrl;
struct packed {
logic d;
logic de;
} ss_level;
struct packed {
logic [3:0] d;
logic de;
} ss_delay;
struct packed {
logic [2:0] d;
logic de;
} clk_div;
} spi_hw2reg_ctrl0_reg_t;
typedef struct packed {
struct packed {
logic d;
} tx_fifo_full;
struct packed {
logic d;
} tx_fifo_empty;
struct packed {
logic d;
} rx_fifo_full;
struct packed {
logic d;
} rx_fifo_empty;
struct packed {
logic d;
} busy;
} spi_hw2reg_status_reg_t;
typedef struct packed {
logic [7:0] d;
} spi_hw2reg_rxdata_reg_t;
// Register -> HW type
typedef struct packed {
spi_reg2hw_ctrl0_reg_t ctrl0; // [53:23]
spi_reg2hw_status_reg_t status; // [22:18]
spi_reg2hw_txdata_reg_t txdata; // [17:9]
spi_reg2hw_rxdata_reg_t rxdata; // [8:0]
} spi_reg2hw_t;
// HW -> register type
typedef struct packed {
spi_hw2reg_ctrl0_reg_t ctrl0; // [43:13]
spi_hw2reg_status_reg_t status; // [12:8]
spi_hw2reg_rxdata_reg_t rxdata; // [7:0]
} spi_hw2reg_t;
// Register offsets
parameter logic [BlockAw-1:0] SPI_CTRL0_OFFSET = 4'h0;
parameter logic [BlockAw-1:0] SPI_STATUS_OFFSET = 4'h4;
parameter logic [BlockAw-1:0] SPI_TXDATA_OFFSET = 4'h8;
parameter logic [BlockAw-1:0] SPI_RXDATA_OFFSET = 4'hc;
// Reset values for hwext registers and their fields
parameter logic [4:0] SPI_STATUS_RESVAL = 5'h0;
parameter logic [7:0] SPI_RXDATA_RESVAL = 8'h0;
// Register index
typedef enum int {
SPI_CTRL0,
SPI_STATUS,
SPI_TXDATA,
SPI_RXDATA
} spi_id_e;
// Register width information to check illegal writes
parameter logic [3:0] SPI_PERMIT [4] = '{
4'b1111, // index[0] SPI_CTRL0
4'b0001, // index[1] SPI_STATUS
4'b0001, // index[2] SPI_TXDATA
4'b0001 // index[3] SPI_RXDATA
};
endpackage

View File

@ -0,0 +1,613 @@
// 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 spi_reg_top (
input logic clk_i,
input logic rst_ni,
// To HW
output spi_reg_pkg::spi_reg2hw_t reg2hw, // Write
input spi_reg_pkg::spi_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 spi_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 ctrl0_we;
logic ctrl0_enable_qs;
logic ctrl0_enable_wd;
logic ctrl0_int_en_qs;
logic ctrl0_int_en_wd;
logic ctrl0_int_pending_qs;
logic ctrl0_int_pending_wd;
logic ctrl0_role_mode_qs;
logic ctrl0_role_mode_wd;
logic [1:0] ctrl0_cp_mode_qs;
logic [1:0] ctrl0_cp_mode_wd;
logic [1:0] ctrl0_spi_mode_qs;
logic [1:0] ctrl0_spi_mode_wd;
logic ctrl0_read_qs;
logic ctrl0_read_wd;
logic ctrl0_msb_first_qs;
logic ctrl0_msb_first_wd;
logic ctrl0_ss_sw_ctrl_qs;
logic ctrl0_ss_sw_ctrl_wd;
logic ctrl0_ss_level_qs;
logic ctrl0_ss_level_wd;
logic [3:0] ctrl0_ss_delay_qs;
logic [3:0] ctrl0_ss_delay_wd;
logic [2:0] ctrl0_clk_div_qs;
logic [2:0] ctrl0_clk_div_wd;
logic status_re;
logic status_tx_fifo_full_qs;
logic status_tx_fifo_empty_qs;
logic status_rx_fifo_full_qs;
logic status_rx_fifo_empty_qs;
logic status_busy_qs;
logic txdata_we;
logic [7:0] txdata_wd;
logic rxdata_re;
logic [7:0] rxdata_qs;
// Register instances
// R[ctrl0]: V(False)
// F[enable]: 0:0
prim_subreg #(
.DW (1),
.SWACCESS("RW"),
.RESVAL (1'h0)
) u_ctrl0_enable (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl0_we),
.wd (ctrl0_enable_wd),
// from internal hardware
.de (hw2reg.ctrl0.enable.de),
.d (hw2reg.ctrl0.enable.d),
// to internal hardware
.qe (reg2hw.ctrl0.enable.qe),
.q (reg2hw.ctrl0.enable.q),
// to register interface (read)
.qs (ctrl0_enable_qs)
);
// F[int_en]: 1:1
prim_subreg #(
.DW (1),
.SWACCESS("RW"),
.RESVAL (1'h0)
) u_ctrl0_int_en (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl0_we),
.wd (ctrl0_int_en_wd),
// from internal hardware
.de (hw2reg.ctrl0.int_en.de),
.d (hw2reg.ctrl0.int_en.d),
// to internal hardware
.qe (reg2hw.ctrl0.int_en.qe),
.q (reg2hw.ctrl0.int_en.q),
// to register interface (read)
.qs (ctrl0_int_en_qs)
);
// F[int_pending]: 2:2
prim_subreg #(
.DW (1),
.SWACCESS("W1C"),
.RESVAL (1'h0)
) u_ctrl0_int_pending (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl0_we),
.wd (ctrl0_int_pending_wd),
// from internal hardware
.de (hw2reg.ctrl0.int_pending.de),
.d (hw2reg.ctrl0.int_pending.d),
// to internal hardware
.qe (reg2hw.ctrl0.int_pending.qe),
.q (reg2hw.ctrl0.int_pending.q),
// to register interface (read)
.qs (ctrl0_int_pending_qs)
);
// F[role_mode]: 3:3
prim_subreg #(
.DW (1),
.SWACCESS("RW"),
.RESVAL (1'h0)
) u_ctrl0_role_mode (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl0_we),
.wd (ctrl0_role_mode_wd),
// from internal hardware
.de (hw2reg.ctrl0.role_mode.de),
.d (hw2reg.ctrl0.role_mode.d),
// to internal hardware
.qe (reg2hw.ctrl0.role_mode.qe),
.q (reg2hw.ctrl0.role_mode.q),
// to register interface (read)
.qs (ctrl0_role_mode_qs)
);
// F[cp_mode]: 5:4
prim_subreg #(
.DW (2),
.SWACCESS("RW"),
.RESVAL (2'h0)
) u_ctrl0_cp_mode (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl0_we),
.wd (ctrl0_cp_mode_wd),
// from internal hardware
.de (hw2reg.ctrl0.cp_mode.de),
.d (hw2reg.ctrl0.cp_mode.d),
// to internal hardware
.qe (reg2hw.ctrl0.cp_mode.qe),
.q (reg2hw.ctrl0.cp_mode.q),
// to register interface (read)
.qs (ctrl0_cp_mode_qs)
);
// F[spi_mode]: 7:6
prim_subreg #(
.DW (2),
.SWACCESS("RW"),
.RESVAL (2'h0)
) u_ctrl0_spi_mode (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl0_we),
.wd (ctrl0_spi_mode_wd),
// from internal hardware
.de (hw2reg.ctrl0.spi_mode.de),
.d (hw2reg.ctrl0.spi_mode.d),
// to internal hardware
.qe (reg2hw.ctrl0.spi_mode.qe),
.q (reg2hw.ctrl0.spi_mode.q),
// to register interface (read)
.qs (ctrl0_spi_mode_qs)
);
// F[read]: 8:8
prim_subreg #(
.DW (1),
.SWACCESS("RW"),
.RESVAL (1'h0)
) u_ctrl0_read (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl0_we),
.wd (ctrl0_read_wd),
// from internal hardware
.de (hw2reg.ctrl0.read.de),
.d (hw2reg.ctrl0.read.d),
// to internal hardware
.qe (reg2hw.ctrl0.read.qe),
.q (reg2hw.ctrl0.read.q),
// to register interface (read)
.qs (ctrl0_read_qs)
);
// F[msb_first]: 9:9
prim_subreg #(
.DW (1),
.SWACCESS("RW"),
.RESVAL (1'h0)
) u_ctrl0_msb_first (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl0_we),
.wd (ctrl0_msb_first_wd),
// from internal hardware
.de (hw2reg.ctrl0.msb_first.de),
.d (hw2reg.ctrl0.msb_first.d),
// to internal hardware
.qe (reg2hw.ctrl0.msb_first.qe),
.q (reg2hw.ctrl0.msb_first.q),
// to register interface (read)
.qs (ctrl0_msb_first_qs)
);
// F[ss_sw_ctrl]: 10:10
prim_subreg #(
.DW (1),
.SWACCESS("RW"),
.RESVAL (1'h0)
) u_ctrl0_ss_sw_ctrl (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl0_we),
.wd (ctrl0_ss_sw_ctrl_wd),
// from internal hardware
.de (hw2reg.ctrl0.ss_sw_ctrl.de),
.d (hw2reg.ctrl0.ss_sw_ctrl.d),
// to internal hardware
.qe (reg2hw.ctrl0.ss_sw_ctrl.qe),
.q (reg2hw.ctrl0.ss_sw_ctrl.q),
// to register interface (read)
.qs (ctrl0_ss_sw_ctrl_qs)
);
// F[ss_level]: 11:11
prim_subreg #(
.DW (1),
.SWACCESS("RW"),
.RESVAL (1'h0)
) u_ctrl0_ss_level (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl0_we),
.wd (ctrl0_ss_level_wd),
// from internal hardware
.de (hw2reg.ctrl0.ss_level.de),
.d (hw2reg.ctrl0.ss_level.d),
// to internal hardware
.qe (reg2hw.ctrl0.ss_level.qe),
.q (reg2hw.ctrl0.ss_level.q),
// to register interface (read)
.qs (ctrl0_ss_level_qs)
);
// F[ss_delay]: 15:12
prim_subreg #(
.DW (4),
.SWACCESS("RW"),
.RESVAL (4'h0)
) u_ctrl0_ss_delay (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl0_we),
.wd (ctrl0_ss_delay_wd),
// from internal hardware
.de (hw2reg.ctrl0.ss_delay.de),
.d (hw2reg.ctrl0.ss_delay.d),
// to internal hardware
.qe (reg2hw.ctrl0.ss_delay.qe),
.q (reg2hw.ctrl0.ss_delay.q),
// to register interface (read)
.qs (ctrl0_ss_delay_qs)
);
// F[clk_div]: 31:29
prim_subreg #(
.DW (3),
.SWACCESS("RW"),
.RESVAL (3'h0)
) u_ctrl0_clk_div (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl0_we),
.wd (ctrl0_clk_div_wd),
// from internal hardware
.de (hw2reg.ctrl0.clk_div.de),
.d (hw2reg.ctrl0.clk_div.d),
// to internal hardware
.qe (reg2hw.ctrl0.clk_div.qe),
.q (reg2hw.ctrl0.clk_div.q),
// to register interface (read)
.qs (ctrl0_clk_div_qs)
);
// R[status]: V(True)
// F[tx_fifo_full]: 0:0
prim_subreg_ext #(
.DW (1)
) u_status_tx_fifo_full (
.re (status_re),
.we (1'b0),
.wd ('0),
.d (hw2reg.status.tx_fifo_full.d),
.qre (),
.qe (),
.q (reg2hw.status.tx_fifo_full.q),
.qs (status_tx_fifo_full_qs)
);
// F[tx_fifo_empty]: 1:1
prim_subreg_ext #(
.DW (1)
) u_status_tx_fifo_empty (
.re (status_re),
.we (1'b0),
.wd ('0),
.d (hw2reg.status.tx_fifo_empty.d),
.qre (),
.qe (),
.q (reg2hw.status.tx_fifo_empty.q),
.qs (status_tx_fifo_empty_qs)
);
// F[rx_fifo_full]: 2:2
prim_subreg_ext #(
.DW (1)
) u_status_rx_fifo_full (
.re (status_re),
.we (1'b0),
.wd ('0),
.d (hw2reg.status.rx_fifo_full.d),
.qre (),
.qe (),
.q (reg2hw.status.rx_fifo_full.q),
.qs (status_rx_fifo_full_qs)
);
// F[rx_fifo_empty]: 3:3
prim_subreg_ext #(
.DW (1)
) u_status_rx_fifo_empty (
.re (status_re),
.we (1'b0),
.wd ('0),
.d (hw2reg.status.rx_fifo_empty.d),
.qre (),
.qe (),
.q (reg2hw.status.rx_fifo_empty.q),
.qs (status_rx_fifo_empty_qs)
);
// F[busy]: 4:4
prim_subreg_ext #(
.DW (1)
) u_status_busy (
.re (status_re),
.we (1'b0),
.wd ('0),
.d (hw2reg.status.busy.d),
.qre (),
.qe (),
.q (reg2hw.status.busy.q),
.qs (status_busy_qs)
);
// R[txdata]: V(False)
prim_subreg #(
.DW (8),
.SWACCESS("WO"),
.RESVAL (8'h0)
) u_txdata (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (txdata_we),
.wd (txdata_wd),
// from internal hardware
.de (1'b0),
.d ('0),
// to internal hardware
.qe (reg2hw.txdata.qe),
.q (reg2hw.txdata.q),
// to register interface (read)
.qs ()
);
// R[rxdata]: V(True)
prim_subreg_ext #(
.DW (8)
) u_rxdata (
.re (rxdata_re),
.we (1'b0),
.wd ('0),
.d (hw2reg.rxdata.d),
.qre (reg2hw.rxdata.re),
.qe (),
.q (reg2hw.rxdata.q),
.qs (rxdata_qs)
);
logic [3:0] addr_hit;
always_comb begin
addr_hit = '0;
addr_hit[0] = (reg_addr == SPI_CTRL0_OFFSET);
addr_hit[1] = (reg_addr == SPI_STATUS_OFFSET);
addr_hit[2] = (reg_addr == SPI_TXDATA_OFFSET);
addr_hit[3] = (reg_addr == SPI_RXDATA_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] & (|(SPI_PERMIT[0] & ~reg_be))) |
(addr_hit[1] & (|(SPI_PERMIT[1] & ~reg_be))) |
(addr_hit[2] & (|(SPI_PERMIT[2] & ~reg_be))) |
(addr_hit[3] & (|(SPI_PERMIT[3] & ~reg_be)))));
end
assign ctrl0_we = addr_hit[0] & reg_we & !reg_error;
assign ctrl0_enable_wd = reg_wdata[0];
assign ctrl0_int_en_wd = reg_wdata[1];
assign ctrl0_int_pending_wd = reg_wdata[2];
assign ctrl0_role_mode_wd = reg_wdata[3];
assign ctrl0_cp_mode_wd = reg_wdata[5:4];
assign ctrl0_spi_mode_wd = reg_wdata[7:6];
assign ctrl0_read_wd = reg_wdata[8];
assign ctrl0_msb_first_wd = reg_wdata[9];
assign ctrl0_ss_sw_ctrl_wd = reg_wdata[10];
assign ctrl0_ss_level_wd = reg_wdata[11];
assign ctrl0_ss_delay_wd = reg_wdata[15:12];
assign ctrl0_clk_div_wd = reg_wdata[31:29];
assign status_re = addr_hit[1] & reg_re & !reg_error;
assign txdata_we = addr_hit[2] & reg_we & !reg_error;
assign txdata_wd = reg_wdata[7:0];
assign rxdata_re = addr_hit[3] & 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] = ctrl0_enable_qs;
reg_rdata_next[1] = ctrl0_int_en_qs;
reg_rdata_next[2] = ctrl0_int_pending_qs;
reg_rdata_next[3] = ctrl0_role_mode_qs;
reg_rdata_next[5:4] = ctrl0_cp_mode_qs;
reg_rdata_next[7:6] = ctrl0_spi_mode_qs;
reg_rdata_next[8] = ctrl0_read_qs;
reg_rdata_next[9] = ctrl0_msb_first_qs;
reg_rdata_next[10] = ctrl0_ss_sw_ctrl_qs;
reg_rdata_next[11] = ctrl0_ss_level_qs;
reg_rdata_next[15:12] = ctrl0_ss_delay_qs;
reg_rdata_next[31:29] = ctrl0_clk_div_qs;
end
addr_hit[1]: begin
reg_rdata_next[0] = status_tx_fifo_full_qs;
reg_rdata_next[1] = status_tx_fifo_empty_qs;
reg_rdata_next[2] = status_rx_fifo_full_qs;
reg_rdata_next[3] = status_rx_fifo_empty_qs;
reg_rdata_next[4] = status_busy_qs;
end
addr_hit[2]: begin
reg_rdata_next[7:0] = '0;
end
addr_hit[3]: begin
reg_rdata_next[7:0] = rxdata_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

107
rtl/perips/spi/spi_top.sv Normal file
View File

@ -0,0 +1,107 @@
/*
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 spi_top (
input logic clk_i,
input logic rst_ni,
// SPI引脚信号
input logic spi_clk_i,
output logic spi_clk_o,
output logic spi_clk_oe_o,
input logic spi_ss_i,
output logic spi_ss_o,
output logic spi_ss_oe_o,
input logic spi_dq0_i,
output logic spi_dq0_o,
output logic spi_dq0_oe_o,
input logic spi_dq1_i,
output logic spi_dq1_o,
output logic spi_dq1_oe_o,
input logic spi_dq2_i,
output logic spi_dq2_o,
output logic spi_dq2_oe_o,
input logic spi_dq3_i,
output logic spi_dq3_o,
output logic spi_dq3_oe_o,
// 中断信号
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 gnt_o,
output logic rvalid_o,
output logic [31:0] data_o
);
logic re;
logic we;
logic [31:0] addr;
logic [31:0] reg_rdata;
assign gnt_o = req_i;
// 读信号
assign re = req_i & (!we_i);
// 写信号
assign we = req_i & we_i;
// 去掉基地址
assign addr = {16'h0, addr_i[15:0]};
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
rvalid_o <= '0;
data_o <= '0;
end else begin
rvalid_o <= req_i;
data_o <= reg_rdata;
end
end
spi_core u_spi_core (
.clk_i (clk_i),
.rst_ni (rst_ni),
.spi_clk_o (spi_clk_o),
.spi_clk_oe_o(spi_clk_oe_o),
.spi_ss_o (spi_ss_o),
.spi_ss_oe_o(spi_ss_oe_o),
.spi_dq0_i (spi_dq0_i),
.spi_dq0_o (spi_dq0_o),
.spi_dq0_oe_o(spi_dq0_oe_o),
.spi_dq1_i (spi_dq1_i),
.spi_dq1_o (spi_dq1_o),
.spi_dq1_oe_o(spi_dq1_oe_o),
.spi_dq2_i (spi_dq2_i),
.spi_dq2_o (spi_dq2_o),
.spi_dq2_oe_o(spi_dq2_oe_o),
.spi_dq3_i (spi_dq3_i),
.spi_dq3_o (spi_dq3_o),
.spi_dq3_oe_o(spi_dq3_oe_o),
.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)
);
endmodule

View File

@ -0,0 +1,332 @@
/*
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 spi_transmit_byte (
input logic clk_i,
input logic rst_ni,
input logic start_i, // 开始传输
input logic slave_mode_i, // 0: master, 1: slave.目前暂不支持slave模式!!!
input logic read_i, // 0: write, 1: read
input logic [1:0] spi_mode_i, // 0: SPI, 1: Dual SPI, 2: Quad SPI, 3: SPI
input logic [1:0] cp_mode_i, // [1]表示CPOL, [0]表示CPHA
input logic [7:0] data_i, // 字节输入
input logic [2:0] div_ratio_i, // 分频比
input logic msb_first_i, // 1: MSB, 0: LSB
output logic [7:0] data_o, // 接收到的数据输出
output logic ready_o, // 1: IDLE, 0: 正在传输
output logic data_valid_o, // 数据输出有效
// CLK
input logic spi_clk_i,
output logic spi_clk_o,
output logic spi_clk_oe_o,
// SS(slave模式使用)
input logic spi_ss_i,
// MOSI(DQ0)
input logic spi_dq0_i,
output logic spi_dq0_o,
output logic spi_dq0_oe_o,
// MISO(DQ1)
input logic spi_dq1_i,
output logic spi_dq1_o,
output logic spi_dq1_oe_o,
// DQ2
input logic spi_dq2_i,
output logic spi_dq2_o,
output logic spi_dq2_oe_o,
// DQ3
input logic spi_dq3_i,
output logic spi_dq3_o,
output logic spi_dq3_oe_o
);
localparam MODE_STAND_SPI = 2'b00;
localparam MODE_DUAL_SPI = 2'b01;
localparam MODE_QUAD_SPI = 2'b10;
localparam S_IDLE = 3'b001;
localparam S_DATA = 3'b010;
localparam S_END = 3'b100;
logic tick;
logic [2:0] state_d, state_q;
logic [4:0] edge_cnt_d, edge_cnt_q;
logic [7:0] in_data_d, in_data_q;
logic [7:0] out_data_d, out_data_q;
logic [4:0] total_edge_cnt_d, total_edge_cnt_q;
logic data_valid_d, data_valid_q;
logic ready_d, ready_q;
logic spi_clk_d, spi_clk_q;
logic spi_clk_oe_d, spi_clk_oe_q;
logic spi_dq0_d, spi_dq0_q;
logic spi_dq0_oe_d, spi_dq0_oe_q;
logic spi_dq1_d, spi_dq1_q;
logic spi_dq1_oe_d, spi_dq1_oe_q;
logic spi_dq2_d, spi_dq2_q;
logic spi_dq2_oe_d, spi_dq2_oe_q;
logic spi_dq3_d, spi_dq3_q;
logic spi_dq3_oe_d, spi_dq3_oe_q;
always_comb begin
state_d = state_q;
edge_cnt_d = edge_cnt_q;
in_data_d = in_data_q;
out_data_d = out_data_q;
data_valid_d = data_valid_q;
ready_d = ready_q;
spi_clk_d = spi_clk_q;
case (state_q)
S_IDLE: begin
spi_clk_d = cp_mode_i[1];
data_valid_d = 1'b0;
ready_d = 1'b1;
if (start_i) begin
edge_cnt_d = '0;
ready_d = 1'b0;
state_d = S_DATA;
if (msb_first_i) begin
out_data_d = data_i;
end else begin
out_data_d[7] = data_i[0];
out_data_d[6] = data_i[1];
out_data_d[5] = data_i[2];
out_data_d[4] = data_i[3];
out_data_d[3] = data_i[4];
out_data_d[2] = data_i[5];
out_data_d[1] = data_i[6];
out_data_d[0] = data_i[7];
end
end
end
S_DATA: begin
if (tick) begin
spi_clk_d = ~spi_clk_q;
edge_cnt_d = edge_cnt_q + 1'b1;
// 第奇数个沿(1, 3, 5...)
if (!edge_cnt_q[0]) begin
// 出数据
if (cp_mode_i[0]) begin
// 第一个bit(s)在IDLE状态时已经送出了
if (edge_cnt_q != 5'd0) begin
case (spi_mode_i)
MODE_STAND_SPI: out_data_d = {out_data_q[6:0], 1'b0};
MODE_DUAL_SPI : out_data_d = {out_data_q[5:0], 2'b0};
MODE_QUAD_SPI : out_data_d = {out_data_q[3:0], 4'b0};
default: out_data_d = {out_data_q[6:0], 1'b0};
endcase
end
// 采数据
end else begin
case (spi_mode_i)
MODE_STAND_SPI: in_data_d = {in_data_q[6:0], spi_dq1_i};
MODE_DUAL_SPI : in_data_d = {in_data_q[5:0], spi_dq1_i, spi_dq0_i};
MODE_QUAD_SPI : in_data_d = {in_data_q[3:0], spi_dq3_i, spi_dq2_i, spi_dq1_i, spi_dq0_i};
default : in_data_d = {in_data_q[6:0], spi_dq1_i};
endcase
end
// 第偶数个沿(2, 4, 6...)
end else begin
// 出数据
if (!cp_mode_i[0]) begin
case (spi_mode_i)
MODE_STAND_SPI: out_data_d = {out_data_q[6:0], 1'b0};
MODE_DUAL_SPI : out_data_d = {out_data_q[5:0], 2'b0};
MODE_QUAD_SPI : out_data_d = {out_data_q[3:0], 4'b0};
default: out_data_d = {out_data_q[6:0], 1'b0};
endcase
// 采数据
end else begin
case (spi_mode_i)
MODE_STAND_SPI: in_data_d = {in_data_q[6:0], spi_dq1_i};
MODE_DUAL_SPI : in_data_d = {in_data_q[5:0], spi_dq1_i, spi_dq0_i};
MODE_QUAD_SPI : in_data_d = {in_data_q[3:0], spi_dq3_i, spi_dq2_i, spi_dq1_i, spi_dq0_i};
default : in_data_d = {in_data_q[6:0], spi_dq1_i};
endcase
end
end
// 最后一个沿
if (edge_cnt_q == total_edge_cnt_q) begin
state_d = S_END;
end
end
end
S_END: begin
if (tick) begin
state_d = S_IDLE;
data_valid_d = 1'b1;
if (!msb_first_i) begin
in_data_d[0] = in_data_q[7];
in_data_d[1] = in_data_q[6];
in_data_d[2] = in_data_q[5];
in_data_d[3] = in_data_q[4];
in_data_d[4] = in_data_q[3];
in_data_d[5] = in_data_q[2];
in_data_d[6] = in_data_q[1];
in_data_d[7] = in_data_q[0];
end
end
end
default: ;
endcase
end
always_comb begin
total_edge_cnt_d = 5'd15;
case (spi_mode_i)
MODE_STAND_SPI: total_edge_cnt_d = 5'd15;
MODE_DUAL_SPI : total_edge_cnt_d = 5'd7;
MODE_QUAD_SPI : total_edge_cnt_d = 5'd3;
default: ;
endcase
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
state_q <= S_IDLE;
edge_cnt_q <= '0;
total_edge_cnt_q <= '0;
in_data_q <= '0;
out_data_q <= '0;
data_valid_q <= '0;
ready_q <= '0;
end else begin
state_q <= state_d;
edge_cnt_q <= edge_cnt_d;
total_edge_cnt_q <= total_edge_cnt_d;
in_data_q <= in_data_d;
out_data_q <= out_data_d;
data_valid_q <= data_valid_d;
ready_q <= ready_d;
end
end
always_comb begin
spi_dq0_d = 1'b0;
spi_dq1_d = 1'b0;
spi_dq2_d = 1'b0;
spi_dq3_d = 1'b0;
spi_dq0_oe_d = 1'b0;
spi_dq1_oe_d = 1'b0;
spi_dq2_oe_d = 1'b0;
spi_dq3_oe_d = 1'b0;
case (spi_mode_i)
MODE_STAND_SPI: begin
spi_dq0_d = out_data_d[7];
spi_dq0_oe_d = 1'b1;
end
MODE_DUAL_SPI: begin
spi_dq0_d = out_data_d[6];
spi_dq1_d = out_data_d[7];
if (read_i) begin
spi_dq0_oe_d = 1'b0;
spi_dq1_oe_d = 1'b0;
end else begin
spi_dq0_oe_d = 1'b1;
spi_dq1_oe_d = 1'b1;
end
end
MODE_QUAD_SPI: begin
spi_dq0_d = out_data_d[4];
spi_dq1_d = out_data_d[5];
spi_dq2_d = out_data_d[6];
spi_dq3_d = out_data_d[7];
if (read_i) begin
spi_dq0_oe_d = 1'b0;
spi_dq1_oe_d = 1'b0;
spi_dq2_oe_d = 1'b0;
spi_dq3_oe_d = 1'b0;
end else begin
spi_dq0_oe_d = 1'b1;
spi_dq1_oe_d = 1'b1;
spi_dq2_oe_d = 1'b1;
spi_dq3_oe_d = 1'b1;
end
end
default: ;
endcase
end
assign spi_clk_oe_d = slave_mode_i ? 1'b0 : 1'b1;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
spi_clk_q <= '0;
spi_clk_oe_q <= '0;
spi_dq0_q <= '0;
spi_dq0_oe_q <= '0;
spi_dq1_q <= '0;
spi_dq1_oe_q <= '0;
spi_dq2_q <= '0;
spi_dq2_oe_q <= '0;
spi_dq3_q <= '0;
spi_dq3_oe_q <= '0;
end else begin
spi_clk_q <= spi_clk_d;
spi_clk_oe_q <= spi_clk_oe_d;
spi_dq0_q <= spi_dq0_d;
spi_dq0_oe_q <= spi_dq0_oe_d;
spi_dq1_q <= spi_dq1_d;
spi_dq1_oe_q <= spi_dq1_oe_d;
spi_dq2_q <= spi_dq2_d;
spi_dq2_oe_q <= spi_dq2_oe_d;
spi_dq3_q <= spi_dq3_d;
spi_dq3_oe_q <= spi_dq3_oe_d;
end
end
assign data_o = in_data_q;
assign data_valid_o = data_valid_q;
assign ready_o = ready_q;
assign spi_clk_o = spi_clk_q;
assign spi_clk_oe_o = spi_clk_oe_q;
assign spi_dq0_o = spi_dq0_q;
assign spi_dq0_oe_o = spi_dq0_oe_q;
assign spi_dq1_o = spi_dq1_q;
assign spi_dq1_oe_o = spi_dq1_oe_q;
assign spi_dq2_o = spi_dq2_q;
assign spi_dq2_oe_o = spi_dq2_oe_q;
assign spi_dq3_o = spi_dq3_q;
assign spi_dq3_oe_o = spi_dq3_oe_q;
logic [15:0] ratio = 1 << div_ratio_i;
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

View File

@ -34,6 +34,13 @@ module tinyriscv_soc_top #(
inout wire i2c_scl_pin, // I2C SCL引脚
inout wire i2c_sda_pin, // I2C SDA引脚
inout wire spi_clk_pin, // SPI CLK引脚
inout wire spi_ss_pin, // SPI SS引脚
inout wire spi_dq0_pin, // SPI DQ0(MOSI)引脚
inout wire spi_dq1_pin, // SPI DQ1(MISO)引脚
inout wire spi_dq2_pin, // SPI DQ2引脚
inout wire spi_dq3_pin, // SPI DQ3引脚
inout wire[1:0] gpio_pins, // GPIO引脚1bit代表一个GPIO
`ifdef VERILATOR
@ -49,9 +56,9 @@ module tinyriscv_soc_top #(
localparam int MASTERS = 3; // Number of master ports
`ifdef VERILATOR
localparam int SLAVES = 9; // Number of slave ports
localparam int SLAVES = 10; // Number of slave ports
`else
localparam int SLAVES = 8; // Number of slave ports
localparam int SLAVES = 9; // Number of slave ports
`endif
// masters
@ -68,8 +75,9 @@ module tinyriscv_soc_top #(
localparam int Uart0 = 5;
localparam int Rvic = 6;
localparam int I2c0 = 7;
localparam int Spi0 = 8;
`ifdef VERILATOR
localparam int SimCtrl = 8;
localparam int SimCtrl = 9;
`endif
@ -119,6 +127,7 @@ module tinyriscv_soc_top #(
wire gpio0_irq;
wire gpio1_irq;
wire i2c0_irq;
wire spi0_irq;
wire[GPIO_NUM-1:0] gpio_data_in;
wire[GPIO_NUM-1:0] gpio_oe;
@ -131,6 +140,25 @@ module tinyriscv_soc_top #(
wire i2c_sda_oe;
wire i2c_sda_out;
wire spi_clk_in;
wire spi_clk_oe;
wire spi_clk_out;
wire spi_ss_in;
wire spi_ss_oe;
wire spi_ss_out;
wire spi_dq0_in;
wire spi_dq0_oe;
wire spi_dq0_out;
wire spi_dq1_in;
wire spi_dq1_oe;
wire spi_dq1_out;
wire spi_dq2_in;
wire spi_dq2_oe;
wire spi_dq2_out;
wire spi_dq3_in;
wire spi_dq3_oe;
wire spi_dq3_out;
always @ (*) begin
irq_src = 32'h0;
irq_src[0] = timer0_irq;
@ -138,6 +166,7 @@ module tinyriscv_soc_top #(
irq_src[2] = gpio0_irq;
irq_src[3] = gpio1_irq;
irq_src[4] = i2c0_irq;
irq_src[5] = spi0_irq;
end
`ifdef VERILATOR
@ -328,10 +357,58 @@ module tinyriscv_soc_top #(
.data_o (slave_rdata[I2c0])
);
assign spi_clk_pin = spi_clk_oe ? spi_clk_out : 1'bz;
assign spi_clk_in = spi_clk_pin;
assign spi_ss_pin = spi_ss_oe ? spi_ss_out : 1'bz;
assign spi_ss_in = spi_ss_pin;
assign spi_dq0_pin = spi_dq0_oe ? spi_dq0_out : 1'bz;
assign spi_dq0_in = spi_dq0_pin;
assign spi_dq1_pin = spi_dq1_oe ? spi_dq1_out : 1'bz;
assign spi_dq1_in = spi_dq1_pin;
assign spi_dq2_pin = spi_dq2_oe ? spi_dq2_out : 1'bz;
assign spi_dq2_in = spi_dq2_pin;
assign spi_dq3_pin = spi_dq3_oe ? spi_dq3_out : 1'bz;
assign spi_dq3_in = spi_dq3_pin;
assign slave_addr_mask[Spi0] = `SPI0_ADDR_MASK;
assign slave_addr_base[Spi0] = `SPI0_ADDR_BASE;
// 8.SPI0模块
spi_top spi0(
.clk_i (clk),
.rst_ni (ndmreset_n),
.spi_clk_i (spi_clk_in),
.spi_clk_o (spi_clk_out),
.spi_clk_oe_o(spi_clk_oe),
.spi_ss_i (spi_ss_in),
.spi_ss_o (spi_ss_out),
.spi_ss_oe_o(spi_ss_oe),
.spi_dq0_i (spi_dq0_in),
.spi_dq0_o (spi_dq0_out),
.spi_dq0_oe_o(spi_dq0_oe),
.spi_dq1_i (spi_dq1_in),
.spi_dq1_o (spi_dq1_out),
.spi_dq1_oe_o(spi_dq1_oe),
.spi_dq2_i (spi_dq2_in),
.spi_dq2_o (spi_dq2_out),
.spi_dq2_oe_o(spi_dq2_oe),
.spi_dq3_i (spi_dq3_in),
.spi_dq3_o (spi_dq3_out),
.spi_dq3_oe_o(spi_dq3_oe),
.irq_o (spi0_irq),
.req_i (slave_req[Spi0]),
.we_i (slave_we[Spi0]),
.be_i (slave_be[Spi0]),
.addr_i (slave_addr[Spi0]),
.data_i (slave_wdata[Spi0]),
.gnt_o (slave_gnt[Spi0]),
.rvalid_o (slave_rvalid[Spi0]),
.data_o (slave_rdata[Spi0])
);
`ifdef VERILATOR
assign slave_addr_mask[SimCtrl] = `SIM_CTRL_ADDR_MASK;
assign slave_addr_base[SimCtrl] = `SIM_CTRL_ADDR_BASE;
// 8.仿真控制模块
// 9.仿真控制模块
sim_ctrl u_sim_ctrl(
.clk_i (clk),
.rst_ni (ndmreset_n),