Files
retroSoC/rtl/ip/native/memory/psram.sv
T
maksyuki fb3621e6b8 feat: add QSPI Flash and Flash Read Binder modules
- Introduced  for handling QSPI flash memory operations, including command and address handling.
- Added  to interface with the C++ flash memory implementation, allowing for read operations via DPI-C.
- Created  for the flash memory implementation, supporting initialization, reading, and memory copying.
- Updated  to integrate the new QSPI Flash module and connect it with the existing system.
- Refactored file paths in various filelists and scripts to accommodate the new directory structure under .
- Enhanced the Python scripts for generating memory maps and preparing the environment for the new structure.
- Adjusted test scripts to reflect changes in the directory structure and ensure compatibility with the new modules.
2026-07-20 16:50:57 +08:00

314 lines
9.5 KiB
Systemverilog

// Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// NOTE: for supporting cross page xfer, the max freq of 'sclk' is 84MHz
// when 'sclk' is 144MHz(max, ~6.94ns), according to TRM:
//
// tCLK(min: 6.94ns) is meet, dont have 8'h03 rd oper(min: 30.3ns)
//
// tCH/tCL(min:0.45%, max: 0.55%) tCLK is meet
//
// tKHKL(max: 1.5ns) is meet
//
// tCPH(min: 50ns) -> 50 / 6.94 = ~7.2 so wait cycles need to >= ceil(7.2) = 8,
// so 'r_cfg_wait' = 8 * 2 >= 16 in default, reset value is '18'
// for lower freq, can modify this value for performance tuning
//
// tCEM(max: 8us) is enough long for just 32bits xfer:
// QPI mode: (32(cmd+addr) + 32(data)) / 4 = 16 cycles + 6 wait cycles = 22 cycles
// for min sclk 12MHz, ~83ns * 22 = 1826ns = 1.826us
//
// tCSP(min: 2.5ns) is meet
// sclk keeps 6.94 / 2 = 3.47ns low at least after ce activing
//
// tCHD(min: 20ns) > tACLK + tCLK
// sclk keep 6.94 * 1.5 = 10.41 at least(no meet!)
// need to set 'r_cfg_chd' = ceil(ceil(20 / 6.94) - 1.5) * 2 = 3, reset value is '4'
// for lower freq, can modify this value for performance tuning
//
// tSP(min: 2ns) is meet
// data keeps 6.94 / 2 = 3.47ns low at least befer sclk
`ifndef NMI_PSRAM_DEF_SV
`define NMI_PSRAM_DEF_SV
// verilog_format: off
`define NMI_PSRAM_WAIT 8'h00
`define NMI_PSRAM_CHD 8'h04
`define NMI_PSRAM_INIT 8'h08
// verilog_format: on
`endif
`include "mmap_define.svh"
interface psram_if ();
logic sck_o;
logic [3:0] nss_o;
logic [3:0] io_oe_o;
logic [3:0] io_di_i;
logic [3:0] io_do_o;
logic irq_o;
modport dut(
output sck_o,
output nss_o,
output io_oe_o,
input io_di_i,
output io_do_o,
output irq_o
);
endinterface
module nmi_psram (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
nmi_if.slave nmi,
psram_if.dut psram
// verilog_format: on
);
// verilog_format: off
localparam FSM_IDLE = 6'd0;
localparam FSM_WE_ST = 6'd1;
localparam FSM_WE = 6'd2;
localparam FSM_RD_ST = 6'd3;
localparam FSM_RD = 6'd4;
// verilog_format: on
// reg
logic [ 4:0] r_cfg_wait;
logic [ 2:0] r_cfg_chd;
logic r_cfg_init;
logic s_cfg_reg_sel;
logic s_mem_sel;
logic s_mem_ready;
logic [31:0] s_mem_rdata;
logic r_rd_st;
logic r_wr_st;
logic [ 7:0] r_xfer_data_bit_cnt;
logic [ 5:0] r_fsm_state;
logic [23:0] r_mem_addr;
logic [31:0] r_mem_wdata;
logic s_psram_ce;
logic s_psram_sio_oen;
logic [ 1:0] s_disp_addr_ofst;
logic [ 7:0] s_disp_xfer_bit_cnt;
logic [31:0] s_disp_wdata;
logic s_init_done;
logic s_core_idle;
logic s_mem_valid_re;
// verilog_format: off
assign psram.nss_o[0] = (~s_init_done) || (s_init_done && nmi.addr[24:23] == 2'd0) ? s_psram_ce : 1'b1;
assign psram.nss_o[1] = (~s_init_done) || (s_init_done && nmi.addr[24:23] == 2'd1) ? s_psram_ce : 1'b1;
assign psram.nss_o[2] = (~s_init_done) || (s_init_done && nmi.addr[24:23] == 2'd2) ? s_psram_ce : 1'b1;
assign psram.nss_o[3] = (~s_init_done) || (s_init_done && nmi.addr[24:23] == 2'd3) ? s_psram_ce : 1'b1;
assign psram.io_oe_o[0] = ~s_psram_sio_oen;
assign psram.io_oe_o[1] = ~s_psram_sio_oen;
assign psram.io_oe_o[2] = ~s_psram_sio_oen;
assign psram.io_oe_o[3] = ~s_psram_sio_oen;
assign psram.irq_o = 1'b0;
// verilog_format: on
assign s_mem_sel = `SOC_ADDR_IS_PSRAM(nmi.addr);
assign s_cfg_reg_sel = `SOC_ADDR_IS_NMI_PSRAM(nmi.addr);
assign nmi.ready = s_mem_sel ? s_mem_ready : 1'b1;
always_comb begin
nmi.rdata = '0;
if (s_mem_sel) begin
nmi.rdata = s_mem_rdata;
end else if (nmi.addr[7:0] == `NMI_PSRAM_WAIT) begin
nmi.rdata = {27'd0, r_cfg_wait};
end else if (nmi.addr[7:0] == `NMI_PSRAM_CHD) begin
nmi.rdata = {29'd0, r_cfg_chd};
end else if (nmi.addr[7:0] == `NMI_PSRAM_INIT) begin
nmi.rdata = {31'd0, r_cfg_init};
end
end
// wait cycles(mmio)
always_ff @(posedge clk_i, negedge rst_n_i) begin
if (~rst_n_i) r_cfg_wait <= 5'd18;
else if (nmi.valid && nmi.wstrb[0] && s_cfg_reg_sel && nmi.addr[7:0] == `NMI_PSRAM_WAIT) begin
r_cfg_wait <= nmi.wdata[4:0];
end
end
// extra cycle for tCHD(mmio)
always_ff @(posedge clk_i, negedge rst_n_i) begin
if (~rst_n_i) r_cfg_chd <= 3'd4;
else if (nmi.valid && nmi.wstrb[0] && s_cfg_reg_sel && nmi.addr[7:0] == `NMI_PSRAM_CHD) begin
r_cfg_chd <= nmi.wdata[2:0];
end
end
// init device/switch qpi mode
always_ff @(posedge clk_i, negedge rst_n_i) begin
if (~rst_n_i) r_cfg_init <= '0;
else if (nmi.valid && nmi.wstrb[0] && s_cfg_reg_sel && nmi.addr[7:0] == `NMI_PSRAM_INIT) begin
r_cfg_init <= nmi.wdata[0];
end
end
edge_det_sync_re #(1) u_mem_valid_edge_det_sync_re (
clk_i,
rst_n_i,
nmi.valid,
s_mem_valid_re
);
always_ff @(posedge clk_i, negedge rst_n_i) begin
if (~rst_n_i) begin
r_rd_st <= '0;
r_wr_st <= '0;
r_xfer_data_bit_cnt <= '0;
r_fsm_state <= FSM_IDLE;
r_mem_addr <= '0;
r_mem_wdata <= '0;
end else begin
if (s_mem_sel) begin
/* verilator lint_off CASEINCOMPLETE */
case (r_fsm_state)
FSM_IDLE: begin
if (s_mem_valid_re && (|nmi.wstrb)) begin
r_fsm_state <= FSM_WE_ST;
r_xfer_data_bit_cnt <= s_disp_xfer_bit_cnt;
r_mem_addr <= {1'b0, nmi.addr[22:0]} + {22'd0, s_disp_addr_ofst};
r_mem_wdata <= s_disp_wdata;
end else if (s_mem_valid_re && (~(|nmi.wstrb))) begin
r_fsm_state <= FSM_RD_ST;
r_xfer_data_bit_cnt <= 8'd32;
r_mem_addr <= {1'b0, nmi.addr[22:0]};
r_mem_wdata <= nmi.wdata; // NOTE: no used
end
end
FSM_WE_ST: begin
if (s_core_idle) begin
r_wr_st <= 1'b1;
r_fsm_state <= FSM_WE;
end
end
FSM_WE: begin
r_wr_st <= 1'b0;
if (s_mem_ready) r_fsm_state <= FSM_IDLE;
end
FSM_RD_ST: begin
if (s_core_idle) begin
r_rd_st <= 1'b1;
r_fsm_state <= FSM_RD;
end
end
FSM_RD: begin
r_rd_st <= 1'b0;
if (s_mem_ready) r_fsm_state <= FSM_IDLE;
end
endcase
end
end
end
psram_core u_psram_core (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.cfg_wait_i (r_cfg_wait),
.cfg_chd_i (r_cfg_chd),
.cfg_init_i (r_cfg_init),
.mem_ready_o (s_mem_ready),
.mem_addr_i (r_mem_addr),
.mem_wdata_i (r_mem_wdata),
.mem_rdata_o (s_mem_rdata),
.xfer_data_bit_cnt_i(r_xfer_data_bit_cnt),
.rd_st_i (r_rd_st),
.wr_st_i (r_wr_st),
.init_done_o (s_init_done),
.idle_o (s_core_idle),
.psram_sclk_o (psram.sck_o),
.psram_ce_o (s_psram_ce),
.psram_mosi_i (psram.io_di_i[0]),
.psram_miso_i (psram.io_di_i[1]),
.psram_sio2_i (psram.io_di_i[2]),
.psram_sio3_i (psram.io_di_i[3]),
.psram_mosi_o (psram.io_do_o[0]),
.psram_miso_o (psram.io_do_o[1]),
.psram_sio2_o (psram.io_do_o[2]),
.psram_sio3_o (psram.io_do_o[3]),
.psram_sio_oen_o (s_psram_sio_oen)
);
wr_dispatcher u_wr_dispatcher (
.wstrb_i (nmi.wstrb),
.wdata_i (nmi.wdata),
.addr_ofst_o (s_disp_addr_ofst),
.xfer_bit_cnt_o(s_disp_xfer_bit_cnt),
.wdata_o (s_disp_wdata)
);
endmodule
module wr_dispatcher (
input logic [ 3:0] wstrb_i,
input logic [31:0] wdata_i,
output logic [ 1:0] addr_ofst_o,
output logic [ 7:0] xfer_bit_cnt_o,
output logic [31:0] wdata_o
);
always_comb begin
wdata_o = wdata_i;
case (wstrb_i)
4'b0001: begin
addr_ofst_o = 2'd0;
xfer_bit_cnt_o = 8'd8;
wdata_o[7:0] = wdata_i[7:0];
end
4'b0010: begin
addr_ofst_o = 2'd1;
xfer_bit_cnt_o = 8'd8;
wdata_o[7:0] = wdata_i[15:8];
end
4'b0100: begin
addr_ofst_o = 2'd2;
xfer_bit_cnt_o = 8'd8;
wdata_o[7:0] = wdata_i[23:16];
end
4'b1000: begin
addr_ofst_o = 2'd3;
xfer_bit_cnt_o = 8'd8;
wdata_o[7:0] = wdata_i[31:24];
end
4'b0011: begin
addr_ofst_o = 2'd0;
xfer_bit_cnt_o = 8'd16;
wdata_o[15:0] = wdata_i[15:0];
end
4'b1100: begin
addr_ofst_o = 2'd2;
xfer_bit_cnt_o = 8'd16;
wdata_o[15:0] = wdata_i[31:16];
end
4'b1111: begin
addr_ofst_o = 2'd0;
xfer_bit_cnt_o = 8'd32;
wdata_o[31:0] = wdata_i[31:0];
end
default: begin
addr_ofst_o = 2'd0;
xfer_bit_cnt_o = 8'd32;
wdata_o = wdata_i[31:0];
end
endcase
end
endmodule