rtl:perips: add flash_ctrl module

Signed-off-by: liangkangnan <liangkangnan@163.com>
pull/4/head
liangkangnan 2021-11-02 10:54:37 +08:00
parent 274b19363b
commit 15928977e1
13 changed files with 1978 additions and 22 deletions

View File

@ -32,27 +32,27 @@ set_property PACKAGE_PIN R11 [get_ports {io_pins[8]}]
# SPI DQ3引脚 # SPI DQ3引脚
set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[15]}] 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引脚 # SPI DQ2引脚
set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[14]}] 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引脚 # SPI DQ1引脚
set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[13]}] 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引脚 # SPI DQ0引脚
set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[12]}] 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引脚 # SPI SS引脚
set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[11]}] 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引脚 # SPI CLK引脚
set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[10]}] 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引脚 # GPIO0引脚
set_property IOSTANDARD LVCMOS33 [get_ports {io_pins[7]}] 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]}] 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引脚 # JTAG TCK引脚
set_property IOSTANDARD LVCMOS33 [get_ports jtag_TCK_pin] set_property IOSTANDARD LVCMOS33 [get_ports jtag_TCK_pin]
set_property PACKAGE_PIN N11 [get_ports jtag_TCK_pin] set_property PACKAGE_PIN N11 [get_ports jtag_TCK_pin]

View File

@ -69,6 +69,14 @@
../rtl/perips/pinmux/pinmux_reg_top.sv ../rtl/perips/pinmux/pinmux_reg_top.sv
../rtl/perips/pinmux/pinmux_core.sv ../rtl/perips/pinmux/pinmux_core.sv
../rtl/perips/pinmux/pinmux_top.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.sv
../rtl/sys_bus/obi_interconnect_master_sel.sv ../rtl/sys_bus/obi_interconnect_master_sel.sv
@ -87,3 +95,4 @@
../rtl/utils/prim_subreg_arb.sv ../rtl/utils/prim_subreg_arb.sv
../rtl/utils/prim_subreg_ext.sv ../rtl/utils/prim_subreg_ext.sv
../rtl/utils/prim_filter.sv ../rtl/utils/prim_filter.sv
../rtl/utils/up_counter.sv

View File

@ -25,6 +25,9 @@
// ROM // ROM
`define ROM_ADDR_MASK ~32'hfffff `define ROM_ADDR_MASK ~32'hfffff
`define ROM_ADDR_BASE 32'h00000000 `define ROM_ADDR_BASE 32'h00000000
// Flash
`define FLASH_ADDR_MASK ~32'hffffff
`define FLASH_ADDR_BASE 32'h01000000
// DEBUG // DEBUG
`define DEBUG_ADDR_MASK ~32'hfffff `define DEBUG_ADDR_MASK ~32'hfffff
`define DEBUG_ADDR_BASE 32'h10000000 `define DEBUG_ADDR_BASE 32'h10000000
@ -61,6 +64,9 @@
// Timer2 // Timer2
`define TIMER2_ADDR_MASK ~32'hffff `define TIMER2_ADDR_MASK ~32'hffff
`define TIMER2_ADDR_BASE 32'h0D000000 `define TIMER2_ADDR_BASE 32'h0D000000
// Flash ctrl
`define FLASH_CTRL_ADDR_MASK ~32'hffffff
`define FLASH_CTRL_ADDR_BASE 32'h0E000000
// I2C1 // I2C1
`define I2C1_ADDR_MASK ~32'hffff `define I2C1_ADDR_MASK ~32'hffff
`define I2C1_ADDR_BASE 32'h0B000000 `define I2C1_ADDR_BASE 32'h0B000000

View File

@ -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",
}
]
}
]
}

View File

@ -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

View File

@ -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

View File

@ -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: <reg>_<field>_{wd|we|qs}
// or <reg>_{wd|we|qs} if field == 1 or 0
logic ctrl_we;
logic ctrl_start_qs;
logic ctrl_start_wd;
logic [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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -25,28 +25,32 @@ module tinyriscv_soc_top #(
parameter int UART_NUM = 3, parameter int UART_NUM = 3,
parameter int SPI_NUM = 1 parameter int SPI_NUM = 1
)( )(
input wire clk_50m_i, // 时钟引脚 input wire clk_50m_i, // 时钟引脚
input wire rst_ext_ni, // 复位引脚,低电平有效 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 `ifdef VERILATOR
output wire dump_wave_en_o, // dump wave使能 output wire dump_wave_en_o, // dump wave使能
`endif `endif
input wire jtag_TCK_pin, // JTAG TCK引脚 input wire jtag_TCK_pin, // JTAG TCK引脚
input wire jtag_TMS_pin, // JTAG TMS引脚 input wire jtag_TMS_pin, // JTAG TMS引脚
input wire jtag_TDI_pin, // JTAG TDI引脚 input wire jtag_TDI_pin, // JTAG TDI引脚
output wire jtag_TDO_pin // JTAG TDO引脚 output wire jtag_TDO_pin // JTAG TDO引脚
); );
localparam int MASTERS = 3; // Number of master ports localparam int MASTERS = 3; // Number of master ports
`ifdef VERILATOR `ifdef VERILATOR
localparam int SLAVES = 16; // Number of slave ports localparam int SLAVES = 17; // Number of slave ports
`else `else
localparam int SLAVES = 15; // Number of slave ports localparam int SLAVES = 16; // Number of slave ports
`endif `endif
// masters // masters
@ -70,8 +74,9 @@ module tinyriscv_soc_top #(
localparam int I2c1 = 12; localparam int I2c1 = 12;
localparam int Timer1 = 13; localparam int Timer1 = 13;
localparam int Timer2 = 14; localparam int Timer2 = 14;
localparam int FlashCtrl = 15;
`ifdef VERILATOR `ifdef VERILATOR
localparam int SimCtrl = 15; localparam int SimCtrl = 16;
`endif `endif
wire master_req [MASTERS]; 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_oe[SPI_NUM-1:0];
wire[3:0] spi_dq_out[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 always @ (*) begin
irq_src = 32'h0; irq_src = 32'h0;
@ -202,7 +214,7 @@ module tinyriscv_soc_top #(
.instr_req_o (master_req[CoreI]), .instr_req_o (master_req[CoreI]),
.instr_gnt_i (master_gnt[CoreI]), .instr_gnt_i (master_gnt[CoreI]),
.instr_rvalid_i (master_rvalid[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_rdata_i (master_rdata[CoreI]),
.instr_err_i (1'b0), .instr_err_i (1'b0),
@ -211,7 +223,7 @@ module tinyriscv_soc_top #(
.data_rvalid_i (master_rvalid[CoreD]), .data_rvalid_i (master_rvalid[CoreD]),
.data_we_o (master_we[CoreD]), .data_we_o (master_we[CoreD]),
.data_be_o (master_be[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_wdata_o (master_wdata[CoreD]),
.data_rdata_i (master_rdata[CoreD]), .data_rdata_i (master_rdata[CoreD]),
.data_err_i (1'b0), .data_err_i (1'b0),
@ -222,6 +234,26 @@ module tinyriscv_soc_top #(
.debug_req_i (debug_req) .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_mask[Rom] = `ROM_ADDR_MASK;
assign slave_addr_base[Rom] = `ROM_ADDR_BASE; assign slave_addr_base[Rom] = `ROM_ADDR_BASE;
// 1.指令存储器 // 1.指令存储器
@ -439,7 +471,7 @@ module tinyriscv_soc_top #(
assign slave_addr_mask[I2c1] = `I2C1_ADDR_MASK; assign slave_addr_mask[I2c1] = `I2C1_ADDR_MASK;
assign slave_addr_base[I2c1] = `I2C1_ADDR_BASE; assign slave_addr_base[I2c1] = `I2C1_ADDR_BASE;
// 12.I2C0模块 // 12.I2C1模块
i2c_top i2c1( i2c_top i2c1(
.clk_i (clk), .clk_i (clk),
.rst_ni (ndmreset_n), .rst_ni (ndmreset_n),
@ -548,10 +580,52 @@ module tinyriscv_soc_top #(
.data_o (slave_rdata[Pinmux]) .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 `ifdef VERILATOR
assign slave_addr_mask[SimCtrl] = `SIM_CTRL_ADDR_MASK; assign slave_addr_mask[SimCtrl] = `SIM_CTRL_ADDR_MASK;
assign slave_addr_base[SimCtrl] = `SIM_CTRL_ADDR_BASE; assign slave_addr_base[SimCtrl] = `SIM_CTRL_ADDR_BASE;
// 15.仿真控制模块 // 16.仿真控制模块
sim_ctrl u_sim_ctrl( sim_ctrl u_sim_ctrl(
.clk_i (clk), .clk_i (clk),
.rst_ni (ndmreset_n), .rst_ni (ndmreset_n),