rtl:perips: add spi master
Signed-off-by: liangkangnan <liangkangnan@163.com>pull/4/head
parent
f74f2d8f5d
commit
57690b00bd
|
@ -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]}]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue