rtl:perips: add flash_ctrl module
Signed-off-by: liangkangnan <liangkangnan@163.com>pull/4/head
parent
274b19363b
commit
15928977e1
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -32,6 +32,10 @@ module tinyriscv_soc_top #(
|
|||
|
||||
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使能
|
||||
`endif
|
||||
|
@ -44,9 +48,9 @@ module tinyriscv_soc_top #(
|
|||
|
||||
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),
|
||||
|
|
Loading…
Reference in New Issue