Files
retroSoC/rtl/ip/native/storage/spisd_cache.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

251 lines
7.4 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.
module spisd_cache (
// verilog_format: off
input logic clk_i,
input logic rst_n_i,
input logic mode_i,
input logic init_done_i,
input logic wr_sync_i,
output logic wr_sync_done_o,
input logic fir_clk_edge_i,
output logic [31:0] sd_addr_o,
output logic sd_rd_req_o,
input logic sd_rd_vld_i,
input logic [ 7:0] sd_rd_data_i,
input logic sd_rd_busy_i,
output logic sd_wr_req_o,
input logic sd_wr_data_req_i,
output logic [7:0] sd_wr_data_o,
input logic sd_wr_busy_i,
nmi_if.slave nmi
// verilog_format: on
);
// verilog_format: off
localparam FSM_IDLE = 2'd0;
localparam FSM_COMP_TAG = 2'd1;
localparam FSM_ALLOC = 2'd2;
localparam FSM_WR_BACK = 2'd3;
// verilog_format: on
logic s_cache_mem_hit, s_cache_byp_hit;
logic [6:0] s_cache_index;
logic s_cache_valid_d, s_cache_valid_q;
logic s_cache_dirty_d, s_cache_dirty_q;
logic [31:0] s_cache_tag_d, s_cache_tag_q;
logic [1:0] s_cache_fsm_d, s_cache_fsm_q;
logic [31:0] s_cache_data_d[0:127];
logic [31:0] s_cache_data_q[0:127];
// sd if
logic [7:0] s_sd_wr_data_d, s_sd_wr_data_q;
// common
logic [6:0] s_line_cnt_d, s_line_cnt_q;
logic [1:0] s_word_cnt_d, s_word_cnt_q;
logic [31:0] s_word_data_d, s_word_data_q;
// wr sync
logic s_wr_sync_d, s_wr_sync_q;
// io
assign sd_wr_data_o = s_sd_wr_data_q;
assign nmi.rdata = s_cache_data_q[s_cache_index];
// cache
assign s_cache_index = nmi.addr[8:2];
assign s_cache_mem_hit = ~mode_i && (nmi.addr[27:9] == s_cache_tag_q[18:0]);
assign s_cache_byp_hit = mode_i && (nmi.addr == s_cache_tag_q);
always_comb begin
// cache
s_cache_valid_d = s_cache_valid_q;
s_cache_dirty_d = s_cache_dirty_q;
s_cache_tag_d = s_cache_tag_q;
s_cache_fsm_d = s_cache_fsm_q;
s_cache_data_d = s_cache_data_q;
// intern
s_line_cnt_d = s_line_cnt_q;
s_word_cnt_d = s_word_cnt_q;
s_word_data_d = s_word_data_q;
// sd_if
sd_rd_req_o = '0;
sd_wr_req_o = '0;
s_sd_wr_data_d = s_sd_wr_data_q;
sd_addr_o = '0;
// mem_if
nmi.ready = '0;
// wr sync
wr_sync_done_o = '0;
unique case (s_cache_fsm_q)
FSM_IDLE: begin
if (init_done_i) begin
if (nmi.valid) s_cache_fsm_d = FSM_COMP_TAG;
// sw wr sync
else if (wr_sync_i && s_cache_dirty_q) begin
s_cache_fsm_d = FSM_WR_BACK;
s_cache_dirty_d = 1'b0;
end
end
end
FSM_COMP_TAG: begin
// cache hit
if ((s_cache_mem_hit || s_cache_byp_hit) && s_cache_valid_q) begin
if (s_wr_sync_q) wr_sync_done_o = 1'b1; // wr sync oper
else begin
nmi.ready = 1'b1; // nomral oper
// write oper, set dirty
if (|nmi.wstrb) begin
s_cache_dirty_d = 1'b1;
if (nmi.wstrb[0]) s_cache_data_d[s_cache_index][7:0] = nmi.wdata[7:0];
if (nmi.wstrb[1]) s_cache_data_d[s_cache_index][15:8] = nmi.wdata[15:8];
if (nmi.wstrb[2]) s_cache_data_d[s_cache_index][23:16] = nmi.wdata[23:16];
if (nmi.wstrb[3]) s_cache_data_d[s_cache_index][31:24] = nmi.wdata[31:24];
end
end
s_cache_fsm_d = FSM_IDLE;
end else begin
// need to update tag line info
s_cache_valid_d = 1'b1;
s_cache_dirty_d = |nmi.wstrb;
// tag line is clean
if (s_cache_valid_q == 1'b0 || s_cache_dirty_q == 1'b0) begin
s_cache_fsm_d = FSM_ALLOC;
if (~mode_i) s_cache_tag_d = {13'd0, nmi.addr[27:9]};
else s_cache_tag_d = nmi.addr;
end else begin
// need to flush data into sd card sectors
s_cache_fsm_d = FSM_WR_BACK;
end
end
end
FSM_ALLOC: begin
if (~sd_rd_busy_i) begin
sd_rd_req_o = 1'b1;
sd_addr_o = s_cache_tag_q;
s_line_cnt_d = '0;
s_word_cnt_d = '0;
end else if (fir_clk_edge_i && sd_rd_vld_i) begin
if (s_word_cnt_q == 2'd3) begin
s_word_cnt_d = '0;
s_line_cnt_d = s_line_cnt_q + 1'b1;
s_cache_data_d[s_line_cnt_q] = {sd_rd_data_i, s_word_data_q[31:8]};
if (s_line_cnt_q == 7'd127) s_cache_fsm_d = FSM_COMP_TAG;
end else begin
s_word_cnt_d = s_word_cnt_q + 1'b1;
s_word_data_d = {sd_rd_data_i, s_word_data_q[31:8]};
end
end
end
FSM_WR_BACK: begin
if (~sd_wr_busy_i) begin
sd_wr_req_o = 1'b1;
sd_addr_o = s_cache_tag_q;
s_line_cnt_d = '0;
s_word_cnt_d = '0;
s_word_data_d = s_cache_data_q[0];
end else begin
// 0 1 2 3
if (fir_clk_edge_i && sd_wr_data_req_i) begin
s_sd_wr_data_d = s_word_data_q[7:0];
if (s_word_cnt_q == 2'd3) begin
s_word_cnt_d = '0;
s_line_cnt_d = s_line_cnt_q + 1'b1;
s_word_data_d = s_cache_data_q[s_line_cnt_d];
if (s_line_cnt_q == 7'd127) begin
s_cache_fsm_d = FSM_ALLOC;
if (~mode_i) s_cache_tag_d = {13'd0, nmi.addr[27:9]};
else s_cache_tag_d = nmi.addr;
end
end else begin
s_word_cnt_d = s_word_cnt_q + 1'b1;
s_word_data_d = {8'd0, s_word_data_q[31:8]};
end
end
end
end
endcase
end
dffr #(2) u_cache_fsm_dffr (
clk_i,
rst_n_i,
s_cache_fsm_d,
s_cache_fsm_q
);
dffr #(1) u_cache_valid_dffr (
clk_i,
rst_n_i,
s_cache_valid_d,
s_cache_valid_q
);
dffr #(1) u_cache_dirty_dffr (
clk_i,
rst_n_i,
s_cache_dirty_d,
s_cache_dirty_q
);
dffr #(32) u_cache_tag_dffr (
clk_i,
rst_n_i,
s_cache_tag_d,
s_cache_tag_q
);
dffr #(7) u_line_cnt_dffr (
clk_i,
rst_n_i,
s_line_cnt_d,
s_line_cnt_q
);
dffr #(2) u_word_cnt_dffr (
clk_i,
rst_n_i,
s_word_cnt_d,
s_word_cnt_q
);
dffr #(8) u_s_sd_wr_data_dffr (
clk_i,
rst_n_i,
s_sd_wr_data_d,
s_sd_wr_data_q
);
dffr #(32) u_word_data_dffr (
clk_i,
rst_n_i,
s_word_data_d,
s_word_data_q
);
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (~rst_n_i) begin
for (int i = 0; i < 128; ++i) s_cache_data_q[i] <= '0;
end else s_cache_data_q <= s_cache_data_d;
end
// wr sync oper
always_comb begin
s_wr_sync_d = s_wr_sync_q;
if (wr_sync_i) s_wr_sync_d = 1'b1;
else if (wr_sync_done_o) s_wr_sync_d = 1'b0;
end
dffr #(1) u_wr_sync_dffr (
clk_i,
rst_n_i,
s_wr_sync_d,
s_wr_sync_q
);
endmodule