diff --git a/fpga/xilinx/constrs/tinyriscv.xdc b/fpga/xilinx/constrs/tinyriscv.xdc index 7351148..f4e2ee2 100644 --- a/fpga/xilinx/constrs/tinyriscv.xdc +++ b/fpga/xilinx/constrs/tinyriscv.xdc @@ -32,27 +32,27 @@ set_property PACKAGE_PIN R11 [get_ports {io_pins[8]}] # SPI DQ3引脚 set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[15]}] -set_property PACKAGE_PIN P3 [get_ports {io_pins[15]}] +set_property PACKAGE_PIN T14 [get_ports {io_pins[15]}] # SPI DQ2引脚 set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[14]}] -set_property PACKAGE_PIN P4 [get_ports {io_pins[14]}] +set_property PACKAGE_PIN R16 [get_ports {io_pins[14]}] # SPI DQ1引脚 set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[13]}] -set_property PACKAGE_PIN P1 [get_ports {io_pins[13]}] +set_property PACKAGE_PIN R15 [get_ports {io_pins[13]}] # SPI DQ0引脚 set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[12]}] -set_property PACKAGE_PIN N1 [get_ports {io_pins[12]}] +set_property PACKAGE_PIN K13 [get_ports {io_pins[12]}] # SPI SS引脚 set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[11]}] -set_property PACKAGE_PIN M5 [get_ports {io_pins[11]}] +set_property PACKAGE_PIN L14 [get_ports {io_pins[11]}] # SPI CLK引脚 set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[10]}] -set_property PACKAGE_PIN N4 [get_ports {io_pins[10]}] +set_property PACKAGE_PIN M14 [get_ports {io_pins[10]}] # GPIO0引脚 set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[7]}] @@ -73,6 +73,27 @@ set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[5]}] set_property PACKAGE_PIN R6 [get_ports {io_pins[5]}] +# SPI Flash引脚 +# CLK +set_property IOSTANDARD LVCMOS33 [get_ports flash_spi_clk_pin] +set_property PACKAGE_PIN N4 [get_ports flash_spi_clk_pin] +# SS +set_property IOSTANDARD LVCMOS33 [get_ports flash_spi_ss_pin] +set_property PACKAGE_PIN M5 [get_ports flash_spi_ss_pin] +# DQ0 +set_property IOSTANDARD LVCMOS33 [get_ports {flash_spi_dq_pin[0]}] +set_property PACKAGE_PIN N1 [get_ports {flash_spi_dq_pin[0]}] +# DQ1 +set_property IOSTANDARD LVCMOS33 [get_ports {flash_spi_dq_pin[1]}] +set_property PACKAGE_PIN P1 [get_ports {flash_spi_dq_pin[1]}] +# DQ2 +set_property IOSTANDARD LVCMOS33 [get_ports {flash_spi_dq_pin[2]}] +set_property PACKAGE_PIN P4 [get_ports {flash_spi_dq_pin[2]}] +# DQ3 +set_property IOSTANDARD LVCMOS33 [get_ports {flash_spi_dq_pin[3]}] +set_property PACKAGE_PIN P3 [get_ports {flash_spi_dq_pin[3]}] + + # JTAG TCK引脚 set_property IOSTANDARD LVCMOS33 [get_ports jtag_TCK_pin] set_property PACKAGE_PIN N11 [get_ports jtag_TCK_pin] diff --git a/rtl.flist b/rtl.flist index 2a627b5..bb3171f 100644 --- a/rtl.flist +++ b/rtl.flist @@ -69,6 +69,14 @@ ../rtl/perips/pinmux/pinmux_reg_top.sv ../rtl/perips/pinmux/pinmux_core.sv ../rtl/perips/pinmux/pinmux_top.sv +../rtl/perips/flash_ctrl/flash_ctrl_reg_pkg.sv +../rtl/perips/flash_ctrl/flash_n25q/flash_n25q_pkg.sv +../rtl/perips/flash_ctrl/flash_n25q/flash_n25q_tran_pkg.sv +../rtl/perips/flash_ctrl/flash_n25q/flash_n25q_top.sv +../rtl/perips/flash_ctrl/flash_n25q/flash_n25q_tran_seq.sv +../rtl/perips/flash_ctrl/flash_ctrl_top.sv +../rtl/perips/flash_ctrl/flash_ctrl_core.sv +../rtl/perips/flash_ctrl/flash_ctrl_reg_top.sv ../rtl/sys_bus/obi_interconnect.sv ../rtl/sys_bus/obi_interconnect_master_sel.sv @@ -87,3 +95,4 @@ ../rtl/utils/prim_subreg_arb.sv ../rtl/utils/prim_subreg_ext.sv ../rtl/utils/prim_filter.sv +../rtl/utils/up_counter.sv diff --git a/rtl/core/defines.sv b/rtl/core/defines.sv index 425d025..a084ad0 100644 --- a/rtl/core/defines.sv +++ b/rtl/core/defines.sv @@ -25,6 +25,9 @@ // ROM `define ROM_ADDR_MASK ~32'hfffff `define ROM_ADDR_BASE 32'h00000000 +// Flash +`define FLASH_ADDR_MASK ~32'hffffff +`define FLASH_ADDR_BASE 32'h01000000 // DEBUG `define DEBUG_ADDR_MASK ~32'hfffff `define DEBUG_ADDR_BASE 32'h10000000 @@ -61,6 +64,9 @@ // Timer2 `define TIMER2_ADDR_MASK ~32'hffff `define TIMER2_ADDR_BASE 32'h0D000000 +// Flash ctrl +`define FLASH_CTRL_ADDR_MASK ~32'hffffff +`define FLASH_CTRL_ADDR_BASE 32'h0E000000 // I2C1 `define I2C1_ADDR_MASK ~32'hffff `define I2C1_ADDR_BASE 32'h0B000000 diff --git a/rtl/perips/flash_ctrl/flash_ctrl.hjson b/rtl/perips/flash_ctrl/flash_ctrl.hjson new file mode 100644 index 0000000..c42b2e7 --- /dev/null +++ b/rtl/perips/flash_ctrl/flash_ctrl.hjson @@ -0,0 +1,71 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ name: "flash_ctrl", + clocking: [{clock: "clk_i", reset: "rst_ni"}], + bus_interfaces: [ + { protocol: "tlul", direction: "device" } + ], + regwidth: "32", + registers: [ + { name: "CTRL", + desc: "flash_ctrl control register", + swaccess: "rw", + hwaccess: "hrw", + hwqe: "true", + fields: [ + { bits: "0", + name: "START", + desc: "start read or write", + } + { bits: "2:1", + name: "OP_MODE", + desc: "0: read, 1: program, 2: erase, 3: qspi init", + } + { bits: "3", + name: "SW_CTRL", + desc: "0: hardware ctrl, 1: software ctrl", + } + { bits: "4", + name: "PROGRAM_INIT", + desc: "0: not program, 1: prepare for program", + } + { bits: "5", + name: "WRITE_ERROR", + swaccess: "ro", + desc: "0: write succ, 1: write error", + } + { bits: "31:6", + swaccess: "r0w1c", + name: "RESERVED", + desc: "reserved, not use", + } + ] + } + { name: "ADDR", + desc: "flash_ctrl address register", + swaccess: "rw", + hwaccess: "hro", + fields: [ + { bits: "22:0", + name: "RW_ADDRESS", + desc: "read or write address", + } + { bits: "31:23", + swaccess: "r0w1c", + name: "RESERVED", + desc: "reserved, not use", + } + ] + } + { name: "DATA", + desc: "flash_ctrl data register", + swaccess: "rw", + hwaccess: "hrw", + fields: [ + { bits: "31:0", + } + ] + } + ] +} diff --git a/rtl/perips/flash_ctrl/flash_ctrl_core.sv b/rtl/perips/flash_ctrl/flash_ctrl_core.sv new file mode 100644 index 0000000..2c416f8 --- /dev/null +++ b/rtl/perips/flash_ctrl/flash_ctrl_core.sv @@ -0,0 +1,279 @@ + /* + 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 flash_ctrl_core ( + input logic clk_i, + input logic rst_ni, + + // SPI引脚信号 + output logic spi_clk_o, + output logic spi_clk_oe_o, + 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, + + // 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 + ); + + import flash_ctrl_reg_pkg::*; + + flash_ctrl_reg_pkg::flash_ctrl_reg2hw_t reg2hw; + flash_ctrl_reg_pkg::flash_ctrl_hw2reg_t hw2reg; + + // 状态 + localparam S_IDLE = 5'b00001; + localparam S_INIT = 5'b00010; + localparam S_READ = 5'b00100; + localparam S_PROGRAM = 5'b01000; + localparam S_ERASE = 5'b10000; + + // 操作 + localparam OP_READ = 2'b00; + localparam OP_PROGRAM = 2'b01; + localparam OP_ERASE = 2'b10; + localparam OP_QSPI_INIT = 2'b11; + + logic [4:0] state_d, state_q; + logic [31:0] op_addr_d, op_addr_q; + logic [31:0] op_data_d, op_data_q; + logic [1:0] op_mode_d, op_mode_q; + logic op_start_d, op_start_q; + logic sw_access_d, sw_access_q; + + logic we; + logic re; + logic [31:0] addr; + logic [31:0] reg_rdata; + logic rvalid; + logic sw_access; + + logic op_ready; + logic op_idle, op_idle_q; + logic op_error; + logic [31:0] op_rdata; + + logic sw_start; + logic [1:0] sw_op_mode; + logic sw_ctrl; + logic sw_program_init; + logic sw_program_init_q; + logic [31:0] sw_rw_addr; + logic [31:0] sw_rw_data; + + logic hw_write_start_bit_en; + logic hw_write_start_bit_data; + + // 寄存器值 + assign sw_start = reg2hw.ctrl.start.q && reg2hw.ctrl.start.qe; + assign sw_op_mode = reg2hw.ctrl.op_mode.q; + assign sw_ctrl = reg2hw.ctrl.sw_ctrl.q; + assign sw_program_init = reg2hw.ctrl.program_init.q; + assign sw_rw_addr = {9'h0, reg2hw.addr.rw_address.q}; + assign sw_rw_data = reg2hw.data.q; + + always_comb begin + // 操作完成清start位 + if ((op_ready && sw_access_q) || + ((~op_idle_q) && op_idle)) begin + hw_write_start_bit_en = 1'b1; + hw_write_start_bit_data = 1'b0; + end else if (sw_program_init_q && (~sw_program_init)) begin + hw_write_start_bit_en = 1'b1; + hw_write_start_bit_data = 1'b1; + end else begin + hw_write_start_bit_en = 1'b0; + hw_write_start_bit_data = 1'b0; + end + end + + // 硬件清start位 + assign hw2reg.ctrl.start.d = hw_write_start_bit_data; + assign hw2reg.ctrl.start.de = hw_write_start_bit_en; + // 硬件更新data寄存器 + assign hw2reg.data.d = op_rdata; + assign hw2reg.data.de = op_ready && sw_access_q && (state_q == S_READ); + // 硬件更新write result位 + assign hw2reg.ctrl.write_error.d = op_error; + assign hw2reg.ctrl.write_error.de = op_ready && sw_access_q; + + assign we = req_i && we_i; + assign re = req_i && (~we_i); + assign addr = {9'h0, addr_i[22:0]}; + // 软件访问,addr_i[23]=0表示是软件访问,addr_i[23]=1表示硬件访问(取指或者取数据) + assign sw_access = (~addr_i[23]); + + always_comb begin + state_d = state_q; + op_addr_d = op_addr_q; + op_data_d = op_data_q; + op_mode_d = op_mode_q; + op_start_d = 1'b0; + sw_access_d = sw_access_q; + + case (state_q) + S_IDLE: begin + op_data_d = sw_rw_data; + // addr_i[23]表示硬件访问(取指或者取数据) + sw_access_d = sw_access; + // 软件访问(读、编程、QSPI初始化) + if (sw_start && sw_ctrl) begin + if (sw_op_mode == OP_READ) begin + state_d = S_READ; + end else if (sw_op_mode == OP_PROGRAM) begin + state_d = S_PROGRAM; + end else if (sw_op_mode == OP_ERASE) begin + state_d = S_ERASE; + end else begin + state_d = S_INIT; + end + op_addr_d = sw_rw_addr; + op_mode_d = sw_op_mode; + op_start_d = 1'b1; + // 硬件访问(取指、取数据) + end else if (re && (~sw_access)) begin + state_d = OP_READ; + op_addr_d = addr; + op_mode_d = 2'b00; + op_start_d = 1'b1; + end + end + + S_INIT: begin + if (op_ready) begin + state_d = S_IDLE; + end + end + + S_READ: begin + if (op_ready) begin + state_d = S_IDLE; + end + end + + S_PROGRAM: begin + if (op_ready) begin + state_d = S_IDLE; + end + end + + S_ERASE: begin + if (op_ready) 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; + op_addr_q <= '0; + op_data_q <= '0; + op_mode_q <= '0; + op_start_q <= '0; + sw_access_q <= '0; + end else begin + state_q <= state_d; + op_addr_q <= op_addr_d; + op_data_q <= op_data_d; + op_mode_q <= op_mode_d; + op_start_q <= op_start_d; + sw_access_q <= sw_access_d; + end + end + + assign gnt_o = (state_q == S_IDLE) && req_i; + assign rvalid = (sw_access && req_i) || op_ready; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rvalid_o <= '0; + data_o <= '0; + op_idle_q <= 1'b1; + sw_program_init_q <= '0; + end else begin + rvalid_o <= rvalid; + data_o <= (op_ready ? op_rdata : reg_rdata); + op_idle_q <= op_idle; + sw_program_init_q <= sw_program_init; + end + end + + flash_n25q_top u_flash_n25q_top ( + .clk_i, + .rst_ni, + .start_i (op_start_q), + .program_init_i (sw_program_init), + .addr_i (op_addr_q), + .data_i (op_data_q), + .op_mode_i (op_mode_q), + .data_o (op_rdata), + .ready_o (op_ready), + .idle_o (op_idle), + .write_error_o (op_error), + .spi_clk_o, + .spi_clk_oe_o, + .spi_ss_o, + .spi_ss_oe_o, + .spi_dq0_i, + .spi_dq0_o, + .spi_dq0_oe_o, + .spi_dq1_i, + .spi_dq1_o, + .spi_dq1_oe_o, + .spi_dq2_i, + .spi_dq2_o, + .spi_dq2_oe_o, + .spi_dq3_i, + .spi_dq3_o, + .spi_dq3_oe_o + ); + + flash_ctrl_reg_top u_flash_ctrl_reg_top ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .reg2hw (reg2hw), + .hw2reg (hw2reg), + .reg_we (we & sw_access), + .reg_re (re & sw_access), + .reg_wdata (data_i), + .reg_be (be_i), + .reg_addr (addr), + .reg_rdata (reg_rdata) + ); + +endmodule diff --git a/rtl/perips/flash_ctrl/flash_ctrl_reg_pkg.sv b/rtl/perips/flash_ctrl/flash_ctrl_reg_pkg.sv new file mode 100644 index 0000000..c3f69c5 --- /dev/null +++ b/rtl/perips/flash_ctrl/flash_ctrl_reg_pkg.sv @@ -0,0 +1,121 @@ +// 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 flash_ctrl_reg_pkg; + + // Address widths within the block + parameter int BlockAw = 4; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + + typedef struct packed { + struct packed { + logic q; + logic qe; + } start; + struct packed { + logic [1:0] q; + logic qe; + } op_mode; + struct packed { + logic q; + logic qe; + } sw_ctrl; + struct packed { + logic q; + logic qe; + } program_init; + struct packed { + logic q; + logic qe; + } write_error; + struct packed { + logic [25:0] q; + logic qe; + } reserved; + } flash_ctrl_reg2hw_ctrl_reg_t; + + typedef struct packed { + struct packed { + logic [22:0] q; + } rw_address; + struct packed { + logic [8:0] q; + } reserved; + } flash_ctrl_reg2hw_addr_reg_t; + + typedef struct packed { + logic [31:0] q; + } flash_ctrl_reg2hw_data_reg_t; + + typedef struct packed { + struct packed { + logic d; + logic de; + } start; + struct packed { + logic [1:0] d; + logic de; + } op_mode; + struct packed { + logic d; + logic de; + } sw_ctrl; + struct packed { + logic d; + logic de; + } program_init; + struct packed { + logic d; + logic de; + } write_error; + struct packed { + logic [25:0] d; + logic de; + } reserved; + } flash_ctrl_hw2reg_ctrl_reg_t; + + typedef struct packed { + logic [31:0] d; + logic de; + } flash_ctrl_hw2reg_data_reg_t; + + // Register -> HW type + typedef struct packed { + flash_ctrl_reg2hw_ctrl_reg_t ctrl; // [101:64] + flash_ctrl_reg2hw_addr_reg_t addr; // [63:32] + flash_ctrl_reg2hw_data_reg_t data; // [31:0] + } flash_ctrl_reg2hw_t; + + // HW -> register type + typedef struct packed { + flash_ctrl_hw2reg_ctrl_reg_t ctrl; // [70:33] + flash_ctrl_hw2reg_data_reg_t data; // [32:0] + } flash_ctrl_hw2reg_t; + + // Register offsets + parameter logic [BlockAw-1:0] FLASH_CTRL_CTRL_OFFSET = 4'h0; + parameter logic [BlockAw-1:0] FLASH_CTRL_ADDR_OFFSET = 4'h4; + parameter logic [BlockAw-1:0] FLASH_CTRL_DATA_OFFSET = 4'h8; + + // Register index + typedef enum int { + FLASH_CTRL_CTRL, + FLASH_CTRL_ADDR, + FLASH_CTRL_DATA + } flash_ctrl_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] FLASH_CTRL_PERMIT [3] = '{ + 4'b1111, // index[0] FLASH_CTRL_CTRL + 4'b1111, // index[1] FLASH_CTRL_ADDR + 4'b1111 // index[2] FLASH_CTRL_DATA + }; + +endpackage + diff --git a/rtl/perips/flash_ctrl/flash_ctrl_reg_top.sv b/rtl/perips/flash_ctrl/flash_ctrl_reg_top.sv new file mode 100644 index 0000000..58c8842 --- /dev/null +++ b/rtl/perips/flash_ctrl/flash_ctrl_reg_top.sv @@ -0,0 +1,375 @@ +// 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 flash_ctrl_reg_top ( + input logic clk_i, + input logic rst_ni, + + // To HW + output flash_ctrl_reg_pkg::flash_ctrl_reg2hw_t reg2hw, // Write + input flash_ctrl_reg_pkg::flash_ctrl_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 flash_ctrl_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 ctrl_we; + logic ctrl_start_qs; + logic ctrl_start_wd; + logic [1:0] ctrl_op_mode_qs; + logic [1:0] ctrl_op_mode_wd; + logic ctrl_sw_ctrl_qs; + logic ctrl_sw_ctrl_wd; + logic ctrl_program_init_qs; + logic ctrl_program_init_wd; + logic ctrl_write_error_qs; + logic [25:0] ctrl_reserved_wd; + logic addr_we; + logic [22:0] addr_rw_address_qs; + logic [22:0] addr_rw_address_wd; + logic [8:0] addr_reserved_wd; + logic data_we; + logic [31:0] data_qs; + logic [31:0] data_wd; + + // Register instances + // R[ctrl]: V(False) + + // F[start]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_start ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_start_wd), + + // from internal hardware + .de (hw2reg.ctrl.start.de), + .d (hw2reg.ctrl.start.d), + + // to internal hardware + .qe (reg2hw.ctrl.start.qe), + .q (reg2hw.ctrl.start.q), + + // to register interface (read) + .qs (ctrl_start_qs) + ); + + + // F[op_mode]: 2:1 + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_ctrl_op_mode ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_op_mode_wd), + + // from internal hardware + .de (hw2reg.ctrl.op_mode.de), + .d (hw2reg.ctrl.op_mode.d), + + // to internal hardware + .qe (reg2hw.ctrl.op_mode.qe), + .q (reg2hw.ctrl.op_mode.q), + + // to register interface (read) + .qs (ctrl_op_mode_qs) + ); + + + // F[sw_ctrl]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_sw_ctrl ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_sw_ctrl_wd), + + // from internal hardware + .de (hw2reg.ctrl.sw_ctrl.de), + .d (hw2reg.ctrl.sw_ctrl.d), + + // to internal hardware + .qe (reg2hw.ctrl.sw_ctrl.qe), + .q (reg2hw.ctrl.sw_ctrl.q), + + // to register interface (read) + .qs (ctrl_sw_ctrl_qs) + ); + + + // F[program_init]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_program_init ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_program_init_wd), + + // from internal hardware + .de (hw2reg.ctrl.program_init.de), + .d (hw2reg.ctrl.program_init.d), + + // to internal hardware + .qe (reg2hw.ctrl.program_init.qe), + .q (reg2hw.ctrl.program_init.q), + + // to register interface (read) + .qs (ctrl_program_init_qs) + ); + + + // F[write_error]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ctrl_write_error ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (1'b0), + .wd ('0), + + // from internal hardware + .de (hw2reg.ctrl.write_error.de), + .d (hw2reg.ctrl.write_error.d), + + // to internal hardware + .qe (reg2hw.ctrl.write_error.qe), + .q (reg2hw.ctrl.write_error.q), + + // to register interface (read) + .qs (ctrl_write_error_qs) + ); + + + // F[reserved]: 31:6 + prim_subreg #( + .DW (26), + .SWACCESS("W1C"), + .RESVAL (26'h0) + ) u_ctrl_reserved ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_reserved_wd), + + // from internal hardware + .de (hw2reg.ctrl.reserved.de), + .d (hw2reg.ctrl.reserved.d), + + // to internal hardware + .qe (reg2hw.ctrl.reserved.qe), + .q (reg2hw.ctrl.reserved.q), + + // to register interface (read) + .qs () + ); + + + // R[addr]: V(False) + + // F[rw_address]: 22:0 + prim_subreg #( + .DW (23), + .SWACCESS("RW"), + .RESVAL (23'h0) + ) u_addr_rw_address ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (addr_we), + .wd (addr_rw_address_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.addr.rw_address.q), + + // to register interface (read) + .qs (addr_rw_address_qs) + ); + + + // F[reserved]: 31:23 + prim_subreg #( + .DW (9), + .SWACCESS("W1C"), + .RESVAL (9'h0) + ) u_addr_reserved ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (addr_we), + .wd (addr_reserved_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.addr.reserved.q), + + // to register interface (read) + .qs () + ); + + + // R[data]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'h0) + ) u_data ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (data_we), + .wd (data_wd), + + // from internal hardware + .de (hw2reg.data.de), + .d (hw2reg.data.d), + + // to internal hardware + .qe (), + .q (reg2hw.data.q), + + // to register interface (read) + .qs (data_qs) + ); + + + logic [2:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[0] = (reg_addr == FLASH_CTRL_CTRL_OFFSET); + addr_hit[1] = (reg_addr == FLASH_CTRL_ADDR_OFFSET); + addr_hit[2] = (reg_addr == FLASH_CTRL_DATA_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = (reg_we & + ((addr_hit[0] & (|(FLASH_CTRL_PERMIT[0] & ~reg_be))) | + (addr_hit[1] & (|(FLASH_CTRL_PERMIT[1] & ~reg_be))) | + (addr_hit[2] & (|(FLASH_CTRL_PERMIT[2] & ~reg_be))))); + end + + assign ctrl_we = addr_hit[0] & reg_we & !reg_error; + + assign ctrl_start_wd = reg_wdata[0]; + + assign ctrl_op_mode_wd = reg_wdata[2:1]; + + assign ctrl_sw_ctrl_wd = reg_wdata[3]; + + assign ctrl_program_init_wd = reg_wdata[4]; + + assign ctrl_reserved_wd = reg_wdata[31:6]; + assign addr_we = addr_hit[1] & reg_we & !reg_error; + + assign addr_rw_address_wd = reg_wdata[22:0]; + + assign addr_reserved_wd = reg_wdata[31:23]; + assign data_we = addr_hit[2] & reg_we & !reg_error; + + assign data_wd = reg_wdata[31:0]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = ctrl_start_qs; + reg_rdata_next[2:1] = ctrl_op_mode_qs; + reg_rdata_next[3] = ctrl_sw_ctrl_qs; + reg_rdata_next[4] = ctrl_program_init_qs; + reg_rdata_next[5] = ctrl_write_error_qs; + reg_rdata_next[31:6] = '0; + end + + addr_hit[1]: begin + reg_rdata_next[22:0] = addr_rw_address_qs; + reg_rdata_next[31:23] = '0; + end + + addr_hit[2]: begin + reg_rdata_next[31:0] = data_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + // Unused signal tieoff + + // wdata / byte enable are not always fully used + // add a blanket unused statement to handle lint waivers + logic unused_wdata; + logic unused_be; + assign unused_wdata = ^reg_wdata; + assign unused_be = ^reg_be; + +endmodule diff --git a/rtl/perips/flash_ctrl/flash_ctrl_top.sv b/rtl/perips/flash_ctrl/flash_ctrl_top.sv new file mode 100644 index 0000000..278925f --- /dev/null +++ b/rtl/perips/flash_ctrl/flash_ctrl_top.sv @@ -0,0 +1,79 @@ + /* + 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 flash_ctrl_top ( + input logic clk_i, + input logic rst_ni, + + // SPI引脚信号 + output logic spi_clk_o, + output logic spi_clk_oe_o, + 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, + + // 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 + ); + + flash_ctrl_core u_flash_ctrl_core ( + .clk_i, + .rst_ni, + .spi_clk_o, + .spi_clk_oe_o, + .spi_ss_o, + .spi_ss_oe_o, + .spi_dq0_i, + .spi_dq0_o, + .spi_dq0_oe_o, + .spi_dq1_i, + .spi_dq1_o, + .spi_dq1_oe_o, + .spi_dq2_i, + .spi_dq2_o, + .spi_dq2_oe_o, + .spi_dq3_i, + .spi_dq3_o, + .spi_dq3_oe_o, + .req_i, + .we_i, + .be_i, + .addr_i, + .data_i, + .gnt_o, + .rvalid_o, + .data_o + ); + +endmodule diff --git a/rtl/perips/flash_ctrl/flash_n25q/flash_n25q_pkg.sv b/rtl/perips/flash_ctrl/flash_n25q/flash_n25q_pkg.sv new file mode 100644 index 0000000..b396bb3 --- /dev/null +++ b/rtl/perips/flash_ctrl/flash_n25q/flash_n25q_pkg.sv @@ -0,0 +1,54 @@ + /* + 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. + */ + +package flash_n25q_pkg; + + parameter logic [7:0] MANF_ID = 8'h20; + parameter logic [7:0] DEV_ID = 8'hBA; + // dummy个数. bytes = dummy / 2 + parameter logic [3:0] DUMMY_CNT = 4'd10; + // SS引脚延时时钟数 + parameter logic [7:0] SS_DELAY_CNT = 8'd30; + + // 命令 + parameter logic [7:0] CMD_READ_ID = 8'h9F; + parameter logic [7:0] CMD_MULTIO_READ_ID = 8'hAF; + parameter logic [7:0] CMD_READ_QSPI_REG = 8'h65; + parameter logic [7:0] CMD_WRITE_QSPI_REG = 8'h61; + parameter logic [7:0] CMD_READ = 8'h03; + parameter logic [7:0] CMD_READ_STATUS_REG = 8'h05; + parameter logic [7:0] CMD_READ_FLAG_STATUS_REG = 8'h70; + parameter logic [7:0] CMD_CLEAR_FLAG_STATUS_REG = 8'h50; + parameter logic [7:0] CMD_FAST_READ = 8'h0B; + parameter logic [7:0] CMD_PAGE_PROG = 8'h02; + parameter logic [7:0] CMD_SUBSECTOR_ERASE = 8'h20; + parameter logic [7:0] CMD_WRITE_ENABLE = 8'h06; + parameter logic [7:0] CMD_WRITE_DISABLE = 8'h04; + parameter logic [7:0] CMD_READ_DUMMY_REG = 8'h85; + parameter logic [7:0] CMD_WRITE_DUMMY_REG = 8'h81; + + // SPI CPOL/CPHL模式 + parameter logic [1:0] CPOL_0_CPHA_0 = 2'b00; + parameter logic [1:0] CPOL_0_CPHA_1 = 2'b01; + parameter logic [1:0] CPOL_1_CPHA_0 = 2'b10; + parameter logic [1:0] CPOL_1_CPHA_1 = 2'b11; + + // SPI模式 + parameter logic [1:0] MODE_STAND_SPI = 2'b00; + parameter logic [1:0] MODE_DUAL_SPI = 2'b01; + parameter logic [1:0] MODE_QUAD_SPI = 2'b10; + +endpackage diff --git a/rtl/perips/flash_ctrl/flash_n25q/flash_n25q_top.sv b/rtl/perips/flash_ctrl/flash_n25q/flash_n25q_top.sv new file mode 100644 index 0000000..4ef70ae --- /dev/null +++ b/rtl/perips/flash_ctrl/flash_n25q/flash_n25q_top.sv @@ -0,0 +1,461 @@ + /* + 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 flash_n25q_top ( + input logic clk_i, + input logic rst_ni, + + input logic start_i, // 开始操作 + input logic program_init_i, // 编程中 + input logic [31:0] addr_i, // 读或者编程地址 + input logic [31:0] data_i, // 编程数据 + input logic [1:0] op_mode_i, // 哪一种操作 + output logic [31:0] data_o, // 操作完成返回的数据 + output logic ready_o, // 操作完成 + output logic idle_o, // 空闲 + output logic write_error_o, // 空闲 + + // SPI引脚信号 + output logic spi_clk_o, + output logic spi_clk_oe_o, + 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 + ); + + import flash_n25q_tran_pkg::*; + import flash_n25q_pkg::*; + + // 状态 + localparam S_IDLE = 14'h1; // 主状态 + localparam S_READ = 14'h2; // 主状态 + localparam S_PROGRAM = 14'h4; // 主状态 + localparam S_ERASE = 14'h8; // 主状态 + localparam S_WRITE_ENABLE = 14'h10; // 子主状态 + localparam S_WRITE_DISABLE = 14'h20; // 子主状态 + localparam S_CHECK_BUSY = 14'h40; // 子主状态 + localparam S_QSPI_INIT = 14'h80; // 主状态 + localparam S_MULTIIO_READ_ID = 14'h100; // 子主状态 + localparam S_WRITE_DUMMY_REG = 14'h200; // 子主状态 + localparam S_WRITE_QSPI_REG = 14'h400; // 子主状态 + localparam S_WAIT_DATA = 14'h800; // 子主状态 + localparam S_READ_FLAG_STATUS_REG = 14'h1000; // 子主状态 + localparam S_CLEAR_FLAG_STATUS_REG = 14'h2000; // 子主状态 + + logic [13:0] state_d, state_q, state_prev_q, return_state_d, return_state_q; + logic [1:0] spi_mode_d, spi_mode_q; + logic [31:0] rdata_d, rdata_q; + logic write_error_d, write_error_q; + + logic tran_idle; + + flash_n25q_tran_pkg::flash_n25q_tran_req_t tran_req_d, tran_req_q; + flash_n25q_tran_pkg::flash_n25q_tran_resp_t tran_resp; + + + always_comb begin + state_d = state_q; + tran_req_d = tran_req_q; + tran_req_d.start = 1'b0; + return_state_d = return_state_q; + spi_mode_d = spi_mode_q; + rdata_d = rdata_q; + write_error_d = write_error_q; + + case (state_q) + S_IDLE: begin + if (start_i) begin + if (op_mode_i == 2'b00) begin + state_d = S_READ; + end else if (op_mode_i == 2'b01) begin + state_d = S_PROGRAM; + end else if (op_mode_i == 2'b10) begin + state_d = S_ERASE; + end else begin + // 当前不为QSPI模式时才初始化 + if (spi_mode_q != MODE_QUAD_SPI) begin + state_d = S_QSPI_INIT; + end + end + end + end + + // 读数据 + S_READ: begin + // 从其他状态进来 + if (state_q ^ state_prev_q) begin + tran_req_d.start = 1'b1; + tran_req_d.spi_mode = spi_mode_q; + if (spi_mode_q == MODE_QUAD_SPI) begin + tran_req_d.cmd = CMD_FAST_READ; + end else begin + tran_req_d.cmd = CMD_READ; + end + tran_req_d.addr.d = addr_i; + tran_req_d.addr.be = 4'b0111; + tran_req_d.dummy.d = '0; + if (spi_mode_q == MODE_QUAD_SPI) begin + tran_req_d.dummy.cnt = {1'b0, DUMMY_CNT[3:1]}; + end else begin + tran_req_d.dummy.cnt = '0; + end + tran_req_d.data.d = '0; + // 以word为单位,每次操作最多读一个word + tran_req_d.data.be = 4'b1111; + tran_req_d.op = OP_READ; + // 读完成 + end else if (tran_resp.ready) begin + rdata_d = tran_resp.data; + state_d = S_IDLE; + end + end + + // 写使能(命令) + S_WRITE_ENABLE: begin + // 从其他状态进来 + if (state_q ^ state_prev_q) begin + tran_req_d.start = 1'b1; + tran_req_d.spi_mode = spi_mode_q; + tran_req_d.cmd = CMD_WRITE_ENABLE; + tran_req_d.addr.d = '0; + tran_req_d.addr.be = 4'b0000; + tran_req_d.dummy.d = '0; + tran_req_d.dummy.cnt = '0; + tran_req_d.data.d = '0; + tran_req_d.data.be = 4'b0000; + tran_req_d.op = OP_WRITE; + end else begin + if (tran_resp.ready) begin + state_d = return_state_q; + end + end + end + + // 写失能命令 + S_WRITE_DISABLE: begin + // 从其他状态进来 + if (state_q ^ state_prev_q) begin + tran_req_d.start = 1'b1; + tran_req_d.spi_mode = spi_mode_q; + tran_req_d.cmd = CMD_WRITE_DISABLE; + tran_req_d.addr.d = '0; + tran_req_d.addr.be = 4'b0000; + tran_req_d.dummy.d = '0; + tran_req_d.dummy.cnt = '0; + tran_req_d.data.d = '0; + tran_req_d.data.be = 4'b0000; + tran_req_d.op = OP_WRITE; + end else begin + if (tran_resp.ready) begin + state_d = return_state_q; + end + end + end + + // 检查ready位,直到ready位为1才返回 + S_CHECK_BUSY: begin + // 从其他状态进来,或者ready位为0 + if ((state_q ^ state_prev_q) || + (tran_resp.ready && (~tran_resp.data[7]))) begin + tran_req_d.start = 1'b1; + tran_req_d.spi_mode = spi_mode_q; + tran_req_d.cmd = CMD_READ_FLAG_STATUS_REG; + tran_req_d.addr.d = '0; + tran_req_d.addr.be = '0; + tran_req_d.dummy.d = '0; + tran_req_d.dummy.cnt = '0; + tran_req_d.data.d = '0; + tran_req_d.data.be = 4'b0001; + tran_req_d.op = OP_READ; + end else begin + if (tran_resp.ready && (tran_resp.data[7])) begin + state_d = return_state_q; + write_error_d = tran_resp.data[5] | tran_resp.data[4]; + end + end + end + + S_CLEAR_FLAG_STATUS_REG: begin + // 从其他状态进来 + if (state_q ^ state_prev_q) begin + tran_req_d.start = 1'b1; + tran_req_d.spi_mode = spi_mode_q; + tran_req_d.cmd = CMD_CLEAR_FLAG_STATUS_REG; + tran_req_d.addr.d = '0; + tran_req_d.addr.be = 4'b0000; + tran_req_d.dummy.d = '0; + tran_req_d.dummy.cnt = '0; + tran_req_d.data.d = '0; + tran_req_d.data.be = 4'b0000; + tran_req_d.op = OP_WRITE; + end else begin + if (tran_resp.ready) begin + state_d = return_state_q; + end + end + end + + S_WAIT_DATA: begin + if (program_init_i) begin + if (start_i && (op_mode_i == 2'b01)) begin + state_d = S_PROGRAM; + end + end else begin + if (tran_idle) begin + state_d = S_CHECK_BUSY; + return_state_d = S_PROGRAM; + end + end + end + + // 编程 + S_PROGRAM: begin + if (state_prev_q == S_IDLE) begin + state_d = S_WRITE_ENABLE; + return_state_d = S_PROGRAM; + end else if ((state_prev_q == S_WRITE_ENABLE) || + (state_prev_q == S_WAIT_DATA)) begin + tran_req_d.start = 1'b1; + tran_req_d.spi_mode = spi_mode_q; + tran_req_d.cmd = CMD_PAGE_PROG; + tran_req_d.addr.d = addr_i; + tran_req_d.addr.be = 4'b0111; + tran_req_d.dummy.d = '0; + tran_req_d.dummy.cnt = '0; + tran_req_d.data.d = data_i; + tran_req_d.data.be = 4'b1111; + tran_req_d.op = OP_WRITE; + end else if (state_prev_q == S_PROGRAM) begin + if (tran_resp.ready) begin + if (program_init_i) begin + state_d = S_WAIT_DATA; + return_state_d = S_PROGRAM; + end else begin + state_d = S_CHECK_BUSY; + return_state_d = S_PROGRAM; + end + end + end else if (state_prev_q == S_CHECK_BUSY) begin + if (write_error_q) begin + state_d = S_CLEAR_FLAG_STATUS_REG; + return_state_d = S_PROGRAM; + end else begin + state_d = S_IDLE; + end + end else if (state_prev_q == S_CLEAR_FLAG_STATUS_REG) begin + state_d = S_IDLE; + end + end + + // 擦除(子扇区) + S_ERASE: begin + if (state_prev_q == S_IDLE) begin + state_d = S_WRITE_ENABLE; + return_state_d = S_ERASE; + end else if (state_prev_q == S_WRITE_ENABLE) begin + tran_req_d.start = 1'b1; + tran_req_d.spi_mode = spi_mode_q; + tran_req_d.cmd = CMD_SUBSECTOR_ERASE; + tran_req_d.addr.d = addr_i; + tran_req_d.addr.be = 4'b0111; + tran_req_d.dummy.d = '0; + tran_req_d.dummy.cnt = '0; + tran_req_d.data.d = '0; + tran_req_d.data.be = 4'b0000; + tran_req_d.op = OP_WRITE; + end else if (state_prev_q == S_ERASE) begin + if (tran_resp.ready) begin + state_d = S_CHECK_BUSY; + return_state_d = S_ERASE; + end + end else if (state_prev_q == S_CHECK_BUSY) begin + if (write_error_q) begin + state_d = S_CLEAR_FLAG_STATUS_REG; + return_state_d = S_ERASE; + end else begin + state_d = S_IDLE; + end + end else if (state_prev_q == S_CLEAR_FLAG_STATUS_REG) begin + state_d = S_IDLE; + end + end + + // QSPI模式读ID + S_MULTIIO_READ_ID: begin + // 从其他状态进来 + if (state_q ^ state_prev_q) begin + tran_req_d.start = 1'b1; + // 用QSPI模式去try + tran_req_d.spi_mode = MODE_QUAD_SPI; + tran_req_d.cmd = CMD_MULTIO_READ_ID; + tran_req_d.addr.d = '0; + tran_req_d.addr.be = '0; + tran_req_d.dummy.d = '0; + tran_req_d.dummy.cnt = '0; + tran_req_d.data.d = '0; + tran_req_d.data.be = 4'b0001; + tran_req_d.op = OP_READ; + end else begin + if (tran_resp.ready) begin + // 读到的ID正确 + if (tran_resp.data[7:0] == MANF_ID) begin + spi_mode_d = MODE_QUAD_SPI; + state_d = S_IDLE; + end else begin + state_d = return_state_q; + end + end + end + end + + // 设置dummy数 + S_WRITE_DUMMY_REG: begin + // 从其他状态进来 + if (state_q ^ state_prev_q) begin + tran_req_d.start = 1'b1; + tran_req_d.spi_mode = MODE_STAND_SPI; + tran_req_d.cmd = CMD_WRITE_DUMMY_REG; + tran_req_d.addr.d = '0; + tran_req_d.addr.be = 4'b0000; + tran_req_d.dummy.d = '0; + tran_req_d.dummy.cnt = '0; + tran_req_d.data.d = {24'h0, DUMMY_CNT, 4'b1011}; + tran_req_d.data.be = 4'b0001; + tran_req_d.op = OP_WRITE; + end else begin + if (tran_resp.ready) begin + state_d = return_state_q; + end + end + end + + // 设置为QSPI模式 + S_WRITE_QSPI_REG: begin + if (state_q ^ state_prev_q) begin + tran_req_d.start = 1'b1; + tran_req_d.spi_mode = MODE_STAND_SPI; + tran_req_d.cmd = CMD_WRITE_QSPI_REG; + tran_req_d.addr.d = '0; + tran_req_d.addr.be = 4'b0000; + tran_req_d.dummy.d = '0; + tran_req_d.dummy.cnt = '0; + tran_req_d.data.d = {24'h0, 8'b01011111}; + tran_req_d.data.be = 4'b0001; + tran_req_d.op = OP_WRITE; + end else begin + if (tran_resp.ready) begin + state_d = return_state_q; + end + end + end + + // QSPI初始化 + S_QSPI_INIT: begin + if (state_prev_q == S_IDLE) begin + state_d = S_MULTIIO_READ_ID; + return_state_d = S_QSPI_INIT; + end else if (state_prev_q == S_MULTIIO_READ_ID) begin + state_d = S_WRITE_ENABLE; + return_state_d = S_QSPI_INIT; + end else if (state_prev_q == S_WRITE_ENABLE) begin + //state_d = S_WRITE_DUMMY_REG; + state_d = S_WRITE_QSPI_REG; + return_state_d = S_QSPI_INIT; + end else if (state_prev_q == S_WRITE_DUMMY_REG) begin + state_d = S_WRITE_QSPI_REG; + return_state_d = S_QSPI_INIT; + end else if (state_prev_q == S_WRITE_QSPI_REG) begin + spi_mode_d = MODE_QUAD_SPI; + state_d = S_IDLE; + end + end + + default: ; + endcase + end + + assign ready_o = ((state_q == S_IDLE) && (state_prev_q != S_IDLE)) || + ((state_q == S_WAIT_DATA) && (state_prev_q == S_PROGRAM)); + assign idle_o = (state_q == S_IDLE) && tran_idle; + assign data_o = rdata_q; + assign write_error_o = write_error_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + state_q <= S_IDLE; + state_prev_q <= S_IDLE; + tran_req_q <= '0; + return_state_q <= S_IDLE; + spi_mode_q <= MODE_STAND_SPI; + rdata_q <= '0; + write_error_q <= '0; + end else begin + state_q <= state_d; + state_prev_q <= state_q; + tran_req_q <= tran_req_d; + return_state_q <= return_state_d; + spi_mode_q <= spi_mode_d; + rdata_q <= rdata_d; + write_error_q <= write_error_d; + end + end + + flash_n25q_tran_seq #( + .SS_DELAY_CNT(SS_DELAY_CNT), +`ifdef VERILATOR + .CLK_DIV(3'd1), +`else + .CLK_DIV(3'd5), +`endif + .CP_MODE(CPOL_0_CPHA_0), + .MSB_FIRST(1'b1) + ) u_flash_n25q_tran_seq ( + .clk_i, + .rst_ni, + .program_init_i, + .tran_req_i (tran_req_q), + .tran_resp_o (tran_resp), + .idle_o (tran_idle), + .spi_clk_o, + .spi_clk_oe_o, + .spi_ss_o, + .spi_ss_oe_o, + .spi_dq0_i, + .spi_dq0_o, + .spi_dq0_oe_o, + .spi_dq1_i, + .spi_dq1_o, + .spi_dq1_oe_o, + .spi_dq2_i, + .spi_dq2_o, + .spi_dq2_oe_o, + .spi_dq3_i, + .spi_dq3_o, + .spi_dq3_oe_o + ); + +endmodule diff --git a/rtl/perips/flash_ctrl/flash_n25q/flash_n25q_tran_pkg.sv b/rtl/perips/flash_ctrl/flash_n25q/flash_n25q_tran_pkg.sv new file mode 100644 index 0000000..4689557 --- /dev/null +++ b/rtl/perips/flash_ctrl/flash_n25q/flash_n25q_tran_pkg.sv @@ -0,0 +1,52 @@ + /* + 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. + */ + +package flash_n25q_tran_pkg; + + // 操作方式 + typedef enum logic [1:0] { + OP_NOP = 2'h0, + OP_READ = 2'h1, + OP_WRITE = 2'h2 + } tran_op_e; + + // 请求数据 + typedef struct packed { + logic start; + logic [1:0] spi_mode; + logic [7:0] cmd; + struct packed { + logic [31:0] d; + logic [ 3:0] be; + } addr; + struct packed { + logic [7:0] d; + logic [3:0] cnt; + } dummy; + struct packed { + logic [31:0] d; + logic [ 3:0] be; + } data; + tran_op_e op; + } flash_n25q_tran_req_t; + + // 响应数据 + typedef struct packed { + logic ready; + logic [31:0] data; + } flash_n25q_tran_resp_t; + +endpackage diff --git a/rtl/perips/flash_ctrl/flash_n25q/flash_n25q_tran_seq.sv b/rtl/perips/flash_ctrl/flash_n25q/flash_n25q_tran_seq.sv new file mode 100644 index 0000000..4f6912a --- /dev/null +++ b/rtl/perips/flash_ctrl/flash_n25q/flash_n25q_tran_seq.sv @@ -0,0 +1,354 @@ + /* + 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 flash_n25q_tran_seq #( + parameter logic [7:0] SS_DELAY_CNT = 8'd10, + parameter logic [2:0] CLK_DIV = 3'd5, + parameter logic [1:0] CP_MODE = 2'd0, + parameter bit MSB_FIRST = 1'b1 + )( + input logic clk_i, + input logic rst_ni, + + input logic program_init_i, // 编程中 + output logic idle_o, // 空闲 + + input flash_n25q_tran_pkg::flash_n25q_tran_req_t tran_req_i, + output flash_n25q_tran_pkg::flash_n25q_tran_resp_t tran_resp_o, + + // SPI引脚信号 + output logic spi_clk_o, + output logic spi_clk_oe_o, + 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 + ); + + import flash_n25q_tran_pkg::*; + + // 状态 + localparam S_IDLE = 8'h1; + localparam S_SS_LOW = 8'h2; + localparam S_CMD = 8'h4; + localparam S_DATA = 8'h8; + localparam S_WAIT_DATA = 8'h10; + localparam S_ADDR = 8'h20; + localparam S_DUMMY = 8'h40; + localparam S_SS_HIGH = 8'h80; + + logic [7:0] state_d, state_q, state_prev_q; + logic spi_ss_level_d, spi_ss_level_q; + logic [7:0] spi_data_in_d, spi_data_in_q; + logic spi_read_d, spi_read_q; + logic spi_start_d, spi_start_q; + logic [3:0] seq_counter_d, seq_counter_q; + logic [31:0] spi_rdata_d, spi_rdata_q; + + logic [7:0] spi_data_out; + logic spi_data_valid; + + logic [7:0] spi_ss_delay_count; + logic spi_ss_delay_counter_clear; + logic spi_ss_delay_counter_enable_d, spi_ss_delay_counter_enable_q; + + + always_comb begin + state_d = state_q; + spi_ss_delay_counter_enable_d = spi_ss_delay_counter_enable_q; + spi_ss_delay_counter_clear = '0; + spi_ss_level_d = spi_ss_level_q; + spi_data_in_d = spi_data_in_q; + spi_read_d = spi_read_q; + spi_start_d = 1'b0; + seq_counter_d = seq_counter_q; + spi_rdata_d = spi_rdata_q; + + // 每传输完一个字节就将计数加1 + if (spi_data_valid) begin + seq_counter_d = seq_counter_q + 1'b1; + end + + case (state_q) + S_IDLE: begin + // SS引脚默认输出高电平 + spi_ss_level_d = 1'b1; + if (tran_req_i.start && (tran_req_i.op != OP_NOP)) begin + state_d = S_SS_LOW; + end + end + + // 拉低SS引脚 + S_SS_LOW: begin + // 从其他状态进来 + if (state_q ^ state_prev_q) begin + // 清零计数器 + spi_ss_delay_counter_clear = 1'b1; + // 开始计时 + spi_ss_delay_counter_enable_d = 1'b1; + end + // 2倍计时时间到 + if (spi_ss_delay_count == (SS_DELAY_CNT * 2)) begin + spi_ss_level_d = 1'b0; + end + // 3倍计时时间到 + if (spi_ss_delay_count == (SS_DELAY_CNT * 3)) begin + spi_ss_delay_counter_clear = 1'b1; + spi_ss_delay_counter_enable_d = 1'b0; + state_d = S_CMD; + end + end + + // 发送command + S_CMD: begin + // 从其他状态进来 + if (state_q ^ state_prev_q) begin + spi_data_in_d = tran_req_i.cmd; + spi_read_d = 1'b0; + spi_start_d = 1'b1; + end + if (spi_data_valid) begin + // 发送地址 + if (tran_req_i.addr.be != 4'h0) begin + state_d = S_ADDR; + // 发送数据 + end else if (tran_req_i.data.be != 4'h0) begin + state_d = S_DATA; + end else begin + state_d = S_SS_HIGH; + end + end + end + + // 发送地址(3个bytes) + S_ADDR: begin + // 从其他状态进来 + if (state_q ^ state_prev_q) begin + seq_counter_d = '0; + spi_data_in_d = tran_req_i.addr.d[23:16]; + spi_read_d = 1'b0; + spi_start_d = 1'b1; + end + if (spi_data_valid) begin + if (seq_counter_q == 4'd0) begin + spi_data_in_d = tran_req_i.addr.d[15:8]; + spi_read_d = 1'b0; + spi_start_d = 1'b1; + end else if (seq_counter_q == 4'd1) begin + spi_data_in_d = tran_req_i.addr.d[7:0]; + spi_read_d = 1'b0; + spi_start_d = 1'b1; + end else begin + // 需要发送dummy + if (tran_req_i.dummy.cnt != 4'h0) begin + state_d = S_DUMMY; + // 需要发送数据 + end else if (tran_req_i.data.be != 4'h0) begin + state_d = S_DATA; + end else begin + state_d = S_SS_HIGH; + end + end + end + end + + // 发送dummy + S_DUMMY: begin + // 从其他状态进来 + if (state_q ^ state_prev_q) begin + seq_counter_d = '0; + spi_data_in_d = '0; + // 注意: 这里设置为读方式 + spi_read_d = 1'b1; + spi_start_d = 1'b1; + end + if (spi_data_valid) begin + // 发完dummy + if (seq_counter_q == (tran_req_i.dummy.cnt - 1)) begin + // 需要发送数据 + if (tran_req_i.data.be != 4'h0) begin + state_d = S_DATA; + end else begin + state_d = S_SS_HIGH; + end + end else begin + spi_data_in_d = '0; + spi_read_d = 1'b1; + spi_start_d = 1'b1; + end + end + end + + // 等待编程数据 + S_WAIT_DATA: begin + if (program_init_i) begin + if (tran_req_i.start && (tran_req_i.op == OP_WRITE)) begin + state_d = S_DATA; + end + end else begin + state_d = S_SS_HIGH; + end + end + + // 发送或者接收数据 + S_DATA: begin + // 从其他状态进来 + if (state_q ^ state_prev_q) begin + seq_counter_d = '0; + // 读数据 + if (tran_req_i.op == OP_READ) begin + spi_data_in_d = '0; + spi_read_d = 1'b1; + // 发数据 + end else begin + spi_data_in_d = tran_req_i.data.d[7:0]; + spi_read_d = 1'b0; + end + spi_start_d = 1'b1; + end + if (spi_data_valid) begin + if (spi_read_q) begin + // 保存接收到的数据 + spi_rdata_d = {spi_data_out, spi_rdata_q[31:8]}; + end + spi_start_d = 1'b1; + if (seq_counter_q == 4'd0) begin + if (tran_req_i.data.be[3]) begin + spi_data_in_d = tran_req_i.data.d[15:8]; + end else begin + spi_start_d = 1'b0; + state_d = S_SS_HIGH; + end + end else if (seq_counter_q == 4'd1) begin + spi_data_in_d = tran_req_i.data.d[23:16]; + end else if (seq_counter_q == 4'd2) begin + spi_data_in_d = tran_req_i.data.d[31:24]; + end else begin + spi_start_d = 1'b0; + if (program_init_i) begin + state_d = S_WAIT_DATA; + end else begin + state_d = S_SS_HIGH; + end + end + end + end + + // 拉高SS引脚 + S_SS_HIGH: begin + // 从其他状态进来 + if (state_q ^ state_prev_q) begin + spi_ss_delay_counter_clear = 1'b1; + spi_ss_delay_counter_enable_d = 1'b1; + end + if (spi_ss_delay_count == SS_DELAY_CNT) begin + spi_ss_level_d = 1'b1; + spi_ss_delay_counter_clear = 1'b1; + spi_ss_delay_counter_enable_d = 1'b0; + state_d = S_IDLE; + end + end + + default: ; + endcase + end + + assign tran_resp_o.ready = ((state_q == S_IDLE) && (state_prev_q == S_SS_HIGH)) || + ((state_q == S_WAIT_DATA) && (state_prev_q == S_DATA)); + assign tran_resp_o.data = spi_rdata_q; + assign idle_o = (state_q == S_IDLE); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + state_q <= S_IDLE; + state_prev_q <= S_IDLE; + spi_ss_delay_counter_enable_q <= '0; + spi_ss_level_q <= 1'b1; + spi_data_in_q <= '0; + spi_read_q <= '0; + spi_start_q <= '0; + seq_counter_q <= '0; + spi_rdata_q <= '0; + end else begin + state_q <= state_d; + state_prev_q <= state_q; + spi_ss_delay_counter_enable_q <= spi_ss_delay_counter_enable_d; + spi_ss_level_q <= spi_ss_level_d; + spi_data_in_q <= spi_data_in_d; + spi_read_q <= spi_read_d; + spi_start_q <= spi_start_d; + seq_counter_q <= seq_counter_d; + spi_rdata_q <= spi_rdata_d; + end + end + + up_counter #( + .WIDTH(8) + ) spi_ss_delay_counter ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .clear_i (spi_ss_delay_counter_clear), + .en_i (spi_ss_delay_counter_enable_q), + .q_o (spi_ss_delay_count), + .overflow_o () + ); + + // 以byte为单位进行传输 + spi_master u_spi_master ( + .clk_i, + .rst_ni, + .start_i (spi_start_q), + .read_i (spi_read_q), + .data_i (spi_data_in_q), + .spi_mode_i (tran_req_i.spi_mode), + .cp_mode_i (CP_MODE), + .div_ratio_i (CLK_DIV), + .msb_first_i (MSB_FIRST), + .ss_delay_cnt_i (4'd5), + .ss_sw_ctrl_i (1'b1), + .ss_level_i (spi_ss_level_q), + .data_o (spi_data_out), + .ready_o (), + .data_valid_o (spi_data_valid), + .spi_clk_o, + .spi_clk_oe_o, + .spi_ss_o, + .spi_ss_oe_o, + .spi_dq0_i, + .spi_dq0_o, + .spi_dq0_oe_o, + .spi_dq1_i, + .spi_dq1_o, + .spi_dq1_oe_o, + .spi_dq2_i, + .spi_dq2_o, + .spi_dq2_oe_o, + .spi_dq3_i, + .spi_dq3_o, + .spi_dq3_oe_o + ); + +endmodule diff --git a/rtl/top/tinyriscv_soc_top.sv b/rtl/top/tinyriscv_soc_top.sv index bcbfc8b..eb85771 100644 --- a/rtl/top/tinyriscv_soc_top.sv +++ b/rtl/top/tinyriscv_soc_top.sv @@ -25,28 +25,32 @@ module tinyriscv_soc_top #( parameter int UART_NUM = 3, parameter int SPI_NUM = 1 )( - input wire clk_50m_i, // 时钟引脚 - input wire rst_ext_ni, // 复位引脚,低电平有效 + input wire clk_50m_i, // 时钟引脚 + input wire rst_ext_ni, // 复位引脚,低电平有效 - output wire halted_ind_pin, // jtag是否已经halt住CPU,高电平有效 + output wire halted_ind_pin, // jtag是否已经halt住CPU,高电平有效 - inout wire[GPIO_NUM-1:0] io_pins, // IO引脚,1bit代表一个IO + inout wire[GPIO_NUM-1:0] io_pins, // IO引脚,1bit代表一个IO + + output wire flash_spi_clk_pin, // flash spi clk引脚 + output wire flash_spi_ss_pin, // flash spi ss引脚 + inout wire [3:0] flash_spi_dq_pin, // flash spi dq引脚 `ifdef VERILATOR - output wire dump_wave_en_o, // dump wave使能 + output wire dump_wave_en_o, // dump wave使能 `endif - input wire jtag_TCK_pin, // JTAG TCK引脚 - input wire jtag_TMS_pin, // JTAG TMS引脚 - input wire jtag_TDI_pin, // JTAG TDI引脚 - output wire jtag_TDO_pin // JTAG TDO引脚 + input wire jtag_TCK_pin, // JTAG TCK引脚 + input wire jtag_TMS_pin, // JTAG TMS引脚 + input wire jtag_TDI_pin, // JTAG TDI引脚 + output wire jtag_TDO_pin // JTAG TDO引脚 ); localparam int MASTERS = 3; // Number of master ports `ifdef VERILATOR - localparam int SLAVES = 16; // Number of slave ports + localparam int SLAVES = 17; // Number of slave ports `else - localparam int SLAVES = 15; // Number of slave ports + localparam int SLAVES = 16; // Number of slave ports `endif // masters @@ -70,8 +74,9 @@ module tinyriscv_soc_top #( localparam int I2c1 = 12; localparam int Timer1 = 13; localparam int Timer2 = 14; + localparam int FlashCtrl = 15; `ifdef VERILATOR - localparam int SimCtrl = 15; + localparam int SimCtrl = 16; `endif wire master_req [MASTERS]; @@ -161,6 +166,13 @@ module tinyriscv_soc_top #( wire[3:0] spi_dq_oe[SPI_NUM-1:0]; wire[3:0] spi_dq_out[SPI_NUM-1:0]; + wire[31:0] core_instr_addr; + wire[31:0] core_data_addr; + + wire[3:0] flash_spi_dq_in; + wire[3:0] flash_spi_dq_oe; + wire[3:0] flash_spi_dq_out; + // 中断源 always @ (*) begin irq_src = 32'h0; @@ -202,7 +214,7 @@ module tinyriscv_soc_top #( .instr_req_o (master_req[CoreI]), .instr_gnt_i (master_gnt[CoreI]), .instr_rvalid_i (master_rvalid[CoreI]), - .instr_addr_o (master_addr[CoreI]), + .instr_addr_o (core_instr_addr), .instr_rdata_i (master_rdata[CoreI]), .instr_err_i (1'b0), @@ -211,7 +223,7 @@ module tinyriscv_soc_top #( .data_rvalid_i (master_rvalid[CoreD]), .data_we_o (master_we[CoreD]), .data_be_o (master_be[CoreD]), - .data_addr_o (master_addr[CoreD]), + .data_addr_o (core_data_addr), .data_wdata_o (master_wdata[CoreD]), .data_rdata_i (master_rdata[CoreD]), .data_err_i (1'b0), @@ -222,6 +234,26 @@ module tinyriscv_soc_top #( .debug_req_i (debug_req) ); + // 是否访问flash + wire instr_access_flash; + wire data_access_flash; + + assign instr_access_flash = ((core_instr_addr & (`FLASH_ADDR_MASK)) == `FLASH_ADDR_BASE); + assign data_access_flash = ((core_data_addr & (`FLASH_ADDR_MASK)) == `FLASH_ADDR_BASE); + + // 转换后的地址 + wire [31:0] instr_tran_addr; + wire [31:0] data_tran_addr; + + assign instr_tran_addr = (core_instr_addr & (~(`FLASH_CTRL_ADDR_MASK))) | `FLASH_CTRL_ADDR_BASE; + assign data_tran_addr = (core_data_addr & (~(`FLASH_CTRL_ADDR_MASK))) | `FLASH_CTRL_ADDR_BASE; + + // 当访问flash空间时,转去访问flash ctrl模块 + assign master_addr[CoreI] = instr_access_flash ? ({instr_tran_addr[31:24], 1'b1, instr_tran_addr[22:0]}) : + core_instr_addr; + assign master_addr[CoreD] = data_access_flash ? ({data_tran_addr[31:24], 1'b1, data_tran_addr[22:0]}) : + core_data_addr; + assign slave_addr_mask[Rom] = `ROM_ADDR_MASK; assign slave_addr_base[Rom] = `ROM_ADDR_BASE; // 1.指令存储器 @@ -439,7 +471,7 @@ module tinyriscv_soc_top #( assign slave_addr_mask[I2c1] = `I2C1_ADDR_MASK; assign slave_addr_base[I2c1] = `I2C1_ADDR_BASE; - // 12.I2C0模块 + // 12.I2C1模块 i2c_top i2c1( .clk_i (clk), .rst_ni (ndmreset_n), @@ -548,10 +580,52 @@ module tinyriscv_soc_top #( .data_o (slave_rdata[Pinmux]) ); + for (genvar j = 0; j < 4; j = j + 1) begin : g_spi_pin_data + assign flash_spi_dq_pin[j] = flash_spi_dq_oe[j] ? flash_spi_dq_out[j] : 1'bz; +`ifdef VERILATOR + // 调试用,固定输入 + assign flash_spi_dq_in[j] = (j) & 1'b1; +`else + assign flash_spi_dq_in[j] = flash_spi_dq_pin[j]; +`endif + end + + assign slave_addr_mask[FlashCtrl] = `FLASH_CTRL_ADDR_MASK; + assign slave_addr_base[FlashCtrl] = `FLASH_CTRL_ADDR_BASE; + // 15.flash ctrl模块 + flash_ctrl_top flash_ctrl ( + .clk_i (clk), + .rst_ni (ndmreset_n), + .spi_clk_o (flash_spi_clk_pin), + .spi_clk_oe_o (), + .spi_ss_o (flash_spi_ss_pin), + .spi_ss_oe_o (), + .spi_dq0_i (flash_spi_dq_in[0]), + .spi_dq0_o (flash_spi_dq_out[0]), + .spi_dq0_oe_o (flash_spi_dq_oe[0]), + .spi_dq1_i (flash_spi_dq_in[1]), + .spi_dq1_o (flash_spi_dq_out[1]), + .spi_dq1_oe_o (flash_spi_dq_oe[1]), + .spi_dq2_i (flash_spi_dq_in[2]), + .spi_dq2_o (flash_spi_dq_out[2]), + .spi_dq2_oe_o (flash_spi_dq_oe[2]), + .spi_dq3_i (flash_spi_dq_in[3]), + .spi_dq3_o (flash_spi_dq_out[3]), + .spi_dq3_oe_o (flash_spi_dq_oe[3]), + .req_i (slave_req[FlashCtrl]), + .we_i (slave_we[FlashCtrl]), + .be_i (slave_be[FlashCtrl]), + .addr_i (slave_addr[FlashCtrl]), + .data_i (slave_wdata[FlashCtrl]), + .gnt_o (slave_gnt[FlashCtrl]), + .rvalid_o (slave_rvalid[FlashCtrl]), + .data_o (slave_rdata[FlashCtrl]) + ); + `ifdef VERILATOR assign slave_addr_mask[SimCtrl] = `SIM_CTRL_ADDR_MASK; assign slave_addr_base[SimCtrl] = `SIM_CTRL_ADDR_BASE; - // 15.仿真控制模块 + // 16.仿真控制模块 sim_ctrl u_sim_ctrl( .clk_i (clk), .rst_ni (ndmreset_n),