From 57690b00bd2298242b910e3771b41fd29e606ef3 Mon Sep 17 00:00:00 2001 From: liangkangnan Date: Mon, 6 Sep 2021 10:01:56 +0800 Subject: [PATCH] rtl:perips: add spi master Signed-off-by: liangkangnan --- fpga/xilinx/constrs/tinyriscv.xdc | 24 ++ rtl.flist | 6 + rtl/core/defines.sv | 3 + rtl/perips/spi/spi.hjson | 116 ++++++ rtl/perips/spi/spi_core.sv | 243 +++++++++++ rtl/perips/spi/spi_master.sv | 195 +++++++++ rtl/perips/spi/spi_reg_pkg.sv | 210 ++++++++++ rtl/perips/spi/spi_reg_top.sv | 613 ++++++++++++++++++++++++++++ rtl/perips/spi/spi_top.sv | 107 +++++ rtl/perips/spi/spi_transmit_byte.sv | 332 +++++++++++++++ rtl/top/tinyriscv_soc_top.sv | 85 +++- 11 files changed, 1930 insertions(+), 4 deletions(-) create mode 100644 rtl/perips/spi/spi.hjson create mode 100644 rtl/perips/spi/spi_core.sv create mode 100644 rtl/perips/spi/spi_master.sv create mode 100644 rtl/perips/spi/spi_reg_pkg.sv create mode 100644 rtl/perips/spi/spi_reg_top.sv create mode 100644 rtl/perips/spi/spi_top.sv create mode 100644 rtl/perips/spi/spi_transmit_byte.sv diff --git a/fpga/xilinx/constrs/tinyriscv.xdc b/fpga/xilinx/constrs/tinyriscv.xdc index cd4206f..1c85cf1 100644 --- a/fpga/xilinx/constrs/tinyriscv.xdc +++ b/fpga/xilinx/constrs/tinyriscv.xdc @@ -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]}] diff --git a/rtl.flist b/rtl.flist index c82fa25..69c7122 100644 --- a/rtl.flist +++ b/rtl.flist @@ -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 diff --git a/rtl/core/defines.sv b/rtl/core/defines.sv index 2163721..a914c4c 100644 --- a/rtl/core/defines.sv +++ b/rtl/core/defines.sv @@ -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 diff --git a/rtl/perips/spi/spi.hjson b/rtl/perips/spi/spi.hjson new file mode 100644 index 0000000..d03cd24 --- /dev/null +++ b/rtl/perips/spi/spi.hjson @@ -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" } + ] + } + ] +} diff --git a/rtl/perips/spi/spi_core.sv b/rtl/perips/spi/spi_core.sv new file mode 100644 index 0000000..7ac4068 --- /dev/null +++ b/rtl/perips/spi/spi_core.sv @@ -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 diff --git a/rtl/perips/spi/spi_master.sv b/rtl/perips/spi/spi_master.sv new file mode 100644 index 0000000..abdf130 --- /dev/null +++ b/rtl/perips/spi/spi_master.sv @@ -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 diff --git a/rtl/perips/spi/spi_reg_pkg.sv b/rtl/perips/spi/spi_reg_pkg.sv new file mode 100644 index 0000000..f532a63 --- /dev/null +++ b/rtl/perips/spi/spi_reg_pkg.sv @@ -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 + diff --git a/rtl/perips/spi/spi_reg_top.sv b/rtl/perips/spi/spi_reg_top.sv new file mode 100644 index 0000000..d840fb5 --- /dev/null +++ b/rtl/perips/spi/spi_reg_top.sv @@ -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: __{wd|we|qs} + // or _{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 diff --git a/rtl/perips/spi/spi_top.sv b/rtl/perips/spi/spi_top.sv new file mode 100644 index 0000000..d657d2d --- /dev/null +++ b/rtl/perips/spi/spi_top.sv @@ -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 diff --git a/rtl/perips/spi/spi_transmit_byte.sv b/rtl/perips/spi/spi_transmit_byte.sv new file mode 100644 index 0000000..f2babe1 --- /dev/null +++ b/rtl/perips/spi/spi_transmit_byte.sv @@ -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 diff --git a/rtl/top/tinyriscv_soc_top.sv b/rtl/top/tinyriscv_soc_top.sv index 68106c1..eed061b 100644 --- a/rtl/top/tinyriscv_soc_top.sv +++ b/rtl/top/tinyriscv_soc_top.sv @@ -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),