diff --git a/rtl.flist b/rtl.flist index 330ede5..6745a04 100644 --- a/rtl.flist +++ b/rtl.flist @@ -69,14 +69,11 @@ ../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/perips/xip/xip_top.sv +../rtl/perips/xip/xip_core.sv +../rtl/perips/xip/xip_w25q64_ctrl.sv +../rtl/perips/xip/spi_master_transmit.sv +../rtl/perips/bootrom/bootrom_top.sv ../rtl/sys_bus/obi_interconnect.sv ../rtl/sys_bus/obi_interconnect_master_sel.sv diff --git a/rtl/perips/bootrom/bootrom_top.sv b/rtl/perips/bootrom/bootrom_top.sv new file mode 100644 index 0000000..6cd12d1 --- /dev/null +++ b/rtl/perips/bootrom/bootrom_top.sv @@ -0,0 +1,74 @@ + /* + Copyright 2023 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 bootrom_top( + input wire clk_i, + input wire rst_ni, + + input wire req_i, + input wire we_i, + input wire [ 3:0] be_i, + input wire [31:0] addr_i, + input wire [31:0] data_i, + output wire gnt_o, + output wire rvalid_o, + output wire [31:0] data_o + ); + + localparam RomSize = 4; + + wire [RomSize-1:0][31:0] mem; + + assign mem = { + 32'h0000006f, + 32'h000500e7, + 32'h00451513, + 32'h00200537 + }; + + reg [5:0] addr_q; + + always @(posedge clk_i) begin + if (req_i) begin + addr_q <= addr_i[7:2]; + end + end + + reg rvalid_q; + + always @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rvalid_q <= 1'b0; + end else begin + rvalid_q <= req_i; + end + end + + reg[31:0] rdata; + + always @(*) begin + rdata = 32'h0; + if (addr_q < 6'd38) begin + rdata = mem[addr_q]; + end + end + + assign gnt_o = req_i; + assign data_o = rdata; + assign rvalid_o = rvalid_q; + +endmodule diff --git a/rtl/perips/xip/spi_master_transmit.sv b/rtl/perips/xip/spi_master_transmit.sv new file mode 100644 index 0000000..99914af --- /dev/null +++ b/rtl/perips/xip/spi_master_transmit.sv @@ -0,0 +1,557 @@ + /* + Copyright 2023 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. + */ + +// SS信号由上层模块控制 +module spi_master_transmit ( + input logic clk_i, + input logic rst_ni, + + input logic start_i, // 开始传输 + input logic read_i, // 0: write, 1: read + input logic [1:0] spi_mode_i, // 0: Standard SPI, 1: Dual SPI, 2: Quad SPI, 3: Standard SPI + input logic [1:0] cp_mode_i, // [1]表示CPOL, [0]表示CPHA + input logic [1:0] data_width_i, // 数据宽度, 0: 8bits, 1: 16bits, 2: 32bits, 3: 8bits + input logic [31:0] data_i, // 数据输入 + input logic [2:0] div_ratio_i, // 分频比(2 ^ div_ratio_i) + input logic msb_first_i, // 1: MSB, 0: LSB + output logic [31:0] data_o, // 接收到的数据输出 + output logic idle_o, // 1: IDLE, 0: 正在传输 + output logic data_valid_o, // 数据输出有效标志 + + // CLK + input logic spi_clk_i, + output logic spi_clk_o, + output logic spi_clk_oe_o, + // SS(slave模式使用) + input logic spi_ss_i, + // MOSI(DQ0) + input logic spi_dq0_i, + output logic spi_dq0_o, + output logic spi_dq0_oe_o, + // MISO(DQ1) + input logic spi_dq1_i, + output logic spi_dq1_o, + output logic spi_dq1_oe_o, + // DQ2 + input logic spi_dq2_i, + output logic spi_dq2_o, + output logic spi_dq2_oe_o, + // DQ3 + input logic spi_dq3_i, + output logic spi_dq3_o, + output logic spi_dq3_oe_o + ); + + localparam MODE_STAND_SPI = 2'b00; + localparam MODE_DUAL_SPI = 2'b01; + localparam MODE_QUAD_SPI = 2'b10; + + localparam S_IDLE = 3'b001; + localparam S_DATA = 3'b010; + localparam S_END = 3'b100; + + logic tick; + + logic [2:0] state_d, state_q; + logic [7:0] edge_cnt_d, edge_cnt_q; + logic [31:0] in_data_d, in_data_q; + logic [31:0] out_data_d, out_data_q; + logic [7:0] total_edge_cnt_d, total_edge_cnt_q; + logic data_valid_d, data_valid_q; + logic ready_d, ready_q; + logic read_d, read_q; + logic [1:0] spi_mode_d, spi_mode_q; + logic [1:0] cp_mode_d, cp_mode_q; + logic [1:0] data_width_d, data_width_q; + logic [2:0] div_ratio_d, div_ratio_q; + logic msb_first_d, msb_first_q; + + logic spi_clk_d, spi_clk_q; + logic spi_clk_oe_d, spi_clk_oe_q; + logic spi_dq0_d, spi_dq0_q; + logic spi_dq0_oe_d, spi_dq0_oe_q; + logic spi_dq1_d, spi_dq1_q; + logic spi_dq1_oe_d, spi_dq1_oe_q; + logic spi_dq2_d, spi_dq2_q; + logic spi_dq2_oe_d, spi_dq2_oe_q; + logic spi_dq3_d, spi_dq3_q; + logic spi_dq3_oe_d, spi_dq3_oe_q; + + + always_comb begin + state_d = state_q; + edge_cnt_d = edge_cnt_q; + in_data_d = in_data_q; + out_data_d = out_data_q; + data_valid_d = data_valid_q; + ready_d = ready_q; + read_d = read_q; + spi_mode_d = spi_mode_q; + cp_mode_d = cp_mode_q; + data_width_d = data_width_q; + div_ratio_d = div_ratio_q; + msb_first_d = msb_first_q; + + spi_clk_d = spi_clk_q; + + case (state_q) + S_IDLE: begin + spi_clk_d = cp_mode_i[1]; + data_valid_d = 1'b0; + ready_d = 1'b1; + if (start_i) begin + edge_cnt_d = '0; + ready_d = 1'b0; + read_d = read_i; + spi_mode_d = spi_mode_i; + cp_mode_d = cp_mode_i; + data_width_d = data_width_i; + div_ratio_d = div_ratio_i; + msb_first_d = msb_first_i; + state_d = S_DATA; + if (msb_first_i) begin + out_data_d = data_i; + end else begin + out_data_d[31] = data_i[0]; + out_data_d[30] = data_i[1]; + out_data_d[29] = data_i[2]; + out_data_d[28] = data_i[3]; + out_data_d[27] = data_i[4]; + out_data_d[26] = data_i[5]; + out_data_d[25] = data_i[6]; + out_data_d[24] = data_i[7]; + out_data_d[23] = data_i[8]; + out_data_d[22] = data_i[9]; + out_data_d[21] = data_i[10]; + out_data_d[20] = data_i[11]; + out_data_d[19] = data_i[12]; + out_data_d[18] = data_i[13]; + out_data_d[17] = data_i[14]; + out_data_d[16] = data_i[15]; + out_data_d[15] = data_i[16]; + out_data_d[14] = data_i[17]; + out_data_d[13] = data_i[18]; + out_data_d[12] = data_i[19]; + out_data_d[11] = data_i[20]; + out_data_d[10] = data_i[21]; + out_data_d[9] = data_i[22]; + out_data_d[8] = data_i[23]; + out_data_d[7] = data_i[24]; + out_data_d[6] = data_i[25]; + out_data_d[5] = data_i[26]; + out_data_d[4] = data_i[27]; + out_data_d[3] = data_i[28]; + out_data_d[2] = data_i[29]; + out_data_d[1] = data_i[30]; + out_data_d[0] = data_i[31]; + end + end + end + + S_DATA: begin + if (tick) begin + spi_clk_d = ~spi_clk_q; + edge_cnt_d = edge_cnt_q + 1'b1; + // 第奇数个沿(1, 3, 5...) + if (!edge_cnt_q[0]) begin + // 出数据 + if (cp_mode_q[0]) begin + // 第一个bit(s)在IDLE状态时已经送出了 + if (edge_cnt_q != 8'd0) begin + case (spi_mode_q) + MODE_STAND_SPI: begin + if (data_width_q == 2'd1) begin + out_data_d = {out_data_q[14:0], 1'b0}; + end else if (data_width_q == 2'd2) begin + out_data_d = {out_data_q[30:0], 1'b0}; + end else begin + out_data_d = {out_data_q[6:0], 1'b0}; + end + end + MODE_DUAL_SPI : begin + if (data_width_q == 2'd1) begin + out_data_d = {out_data_q[13:0], 2'b0}; + end else if (data_width_q == 2'd2) begin + out_data_d = {out_data_q[29:0], 2'b0}; + end else begin + out_data_d = {out_data_q[5:0], 2'b0}; + end + end + MODE_QUAD_SPI : begin + if (data_width_q == 2'd1) begin + out_data_d = {out_data_q[11:0], 4'b0}; + end else if (data_width_q == 2'd2) begin + out_data_d = {out_data_q[27:0], 4'b0}; + end else begin + out_data_d = {out_data_q[3:0], 4'b0}; + end + end + default: out_data_d = {out_data_q[6:0], 1'b0}; + endcase + end + // 采数据 + end else begin + case (spi_mode_q) + MODE_STAND_SPI: begin + if (data_width_q == 2'd1) begin + in_data_d = {in_data_q[14:0], spi_dq1_i}; + end else if (data_width_q == 2'd2) begin + in_data_d = {in_data_q[30:0], spi_dq1_i}; + end else begin + in_data_d = {in_data_q[6:0], spi_dq1_i}; + end + end + MODE_DUAL_SPI : begin + if (data_width_q == 2'd1) begin + in_data_d = {in_data_q[13:0], spi_dq1_i, spi_dq0_i}; + end else if (data_width_q == 2'd2) begin + in_data_d = {in_data_q[29:0], spi_dq1_i, spi_dq0_i}; + end else begin + in_data_d = {in_data_q[5:0], spi_dq1_i, spi_dq0_i}; + end + end + MODE_QUAD_SPI : begin + if (data_width_q == 2'd1) begin + in_data_d = {in_data_q[11:0], spi_dq3_i, spi_dq2_i, spi_dq1_i, spi_dq0_i}; + end else if (data_width_q == 2'd2) begin + in_data_d = {in_data_q[27:0], spi_dq3_i, spi_dq2_i, spi_dq1_i, spi_dq0_i}; + end else begin + in_data_d = {in_data_q[3:0], spi_dq3_i, spi_dq2_i, spi_dq1_i, spi_dq0_i}; + end + end + default: in_data_d = {in_data_q[6:0], spi_dq1_i}; + endcase + end + // 第偶数个沿(2, 4, 6...) + end else begin + // 出数据 + if (!cp_mode_q[0]) begin + case (spi_mode_q) + MODE_STAND_SPI: begin + if (data_width_q == 2'd1) begin + out_data_d = {out_data_q[14:0], 1'b0}; + end else if (data_width_q == 2'd2) begin + out_data_d = {out_data_q[30:0], 1'b0}; + end else begin + out_data_d = {out_data_q[6:0], 1'b0}; + end + end + MODE_DUAL_SPI : begin + if (data_width_q == 2'd1) begin + out_data_d = {out_data_q[13:0], 2'b0}; + end else if (data_width_q == 2'd2) begin + out_data_d = {out_data_q[29:0], 2'b0}; + end else begin + out_data_d = {out_data_q[5:0], 2'b0}; + end + end + MODE_QUAD_SPI : begin + if (data_width_q == 2'd1) begin + out_data_d = {out_data_q[11:0], 4'b0}; + end else if (data_width_q == 2'd2) begin + out_data_d = {out_data_q[27:0], 4'b0}; + end else begin + out_data_d = {out_data_q[3:0], 4'b0}; + end + end + default: out_data_d = {out_data_q[6:0], 1'b0}; + endcase + // 采数据 + end else begin + case (spi_mode_q) + MODE_STAND_SPI: begin + if (data_width_q == 2'd1) begin + in_data_d = {in_data_q[14:0], spi_dq1_i}; + end else if (data_width_q == 2'd2) begin + in_data_d = {in_data_q[30:0], spi_dq1_i}; + end else begin + in_data_d = {in_data_q[6:0], spi_dq1_i}; + end + end + MODE_DUAL_SPI : begin + if (data_width_q == 2'd1) begin + in_data_d = {in_data_q[13:0], spi_dq1_i, spi_dq0_i}; + end else if (data_width_q == 2'd2) begin + in_data_d = {in_data_q[29:0], spi_dq1_i, spi_dq0_i}; + end else begin + in_data_d = {in_data_q[5:0], spi_dq1_i, spi_dq0_i}; + end + end + MODE_QUAD_SPI : begin + if (data_width_q == 2'd1) begin + in_data_d = {in_data_q[11:0], spi_dq3_i, spi_dq2_i, spi_dq1_i, spi_dq0_i}; + end else if (data_width_q == 2'd2) begin + in_data_d = {in_data_q[27:0], spi_dq3_i, spi_dq2_i, spi_dq1_i, spi_dq0_i}; + end else begin + in_data_d = {in_data_q[3:0], spi_dq3_i, spi_dq2_i, spi_dq1_i, spi_dq0_i}; + end + end + default: in_data_d = {in_data_q[6:0], spi_dq1_i}; + endcase + end + end + // 最后一个沿 + if (edge_cnt_q == total_edge_cnt_q) begin + state_d = S_END; + end + end + end + + S_END: begin + if (tick) begin + state_d = S_IDLE; + ready_d = 1'b1; + data_valid_d = 1'b1; + if (!msb_first_q) begin + in_data_d[0] = in_data_q[31]; + in_data_d[1] = in_data_q[30]; + in_data_d[2] = in_data_q[29]; + in_data_d[3] = in_data_q[28]; + in_data_d[4] = in_data_q[27]; + in_data_d[5] = in_data_q[26]; + in_data_d[6] = in_data_q[25]; + in_data_d[7] = in_data_q[24]; + in_data_d[8] = in_data_q[23]; + in_data_d[9] = in_data_q[22]; + in_data_d[10] = in_data_q[21]; + in_data_d[11] = in_data_q[20]; + in_data_d[12] = in_data_q[19]; + in_data_d[13] = in_data_q[18]; + in_data_d[14] = in_data_q[17]; + in_data_d[15] = in_data_q[16]; + in_data_d[16] = in_data_q[15]; + in_data_d[17] = in_data_q[14]; + in_data_d[18] = in_data_q[13]; + in_data_d[19] = in_data_q[12]; + in_data_d[20] = in_data_q[11]; + in_data_d[21] = in_data_q[10]; + in_data_d[22] = in_data_q[9]; + in_data_d[23] = in_data_q[8]; + in_data_d[24] = in_data_q[7]; + in_data_d[25] = in_data_q[6]; + in_data_d[26] = in_data_q[5]; + in_data_d[27] = in_data_q[4]; + in_data_d[28] = in_data_q[3]; + in_data_d[29] = in_data_q[2]; + in_data_d[30] = in_data_q[1]; + in_data_d[31] = in_data_q[0]; + end + end + end + + default: ; + endcase + end + + always_comb begin + total_edge_cnt_d = 8'd63; + + case (spi_mode_q) + MODE_STAND_SPI: begin + if (data_width_q == 2'd1) begin + total_edge_cnt_d = 8'd31; + end else if (data_width_q == 2'd2) begin + total_edge_cnt_d = 8'd63; + end else begin + total_edge_cnt_d = 8'd15; + end + end + MODE_DUAL_SPI : begin + if (data_width_q == 2'd1) begin + total_edge_cnt_d = 8'd15; + end else if (data_width_q == 2'd2) begin + total_edge_cnt_d = 8'd31; + end else begin + total_edge_cnt_d = 8'd7; + end + end + MODE_QUAD_SPI : begin + if (data_width_q == 2'd1) begin + total_edge_cnt_d = 8'd7; + end else if (data_width_q == 2'd2) begin + total_edge_cnt_d = 8'd15; + end else begin + total_edge_cnt_d = 8'd3; + end + end + default: ; + endcase + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + state_q <= S_IDLE; + edge_cnt_q <= '0; + total_edge_cnt_q <= '0; + in_data_q <= '0; + out_data_q <= '0; + data_valid_q <= '0; + ready_q <= '0; + read_q <= '0; + spi_mode_q <= '0; + cp_mode_q <= '0; + data_width_q <= '0; + div_ratio_q <= '0; + msb_first_q <= '0; + end else begin + state_q <= state_d; + edge_cnt_q <= edge_cnt_d; + total_edge_cnt_q <= total_edge_cnt_d; + in_data_q <= in_data_d; + out_data_q <= out_data_d; + data_valid_q <= data_valid_d; + ready_q <= ready_d; + read_q <= read_d; + spi_mode_q <= spi_mode_d; + cp_mode_q <= cp_mode_d; + data_width_q <= data_width_d; + div_ratio_q <= div_ratio_d; + msb_first_q <= msb_first_d; + end + end + + always_comb begin + spi_dq0_d = 1'b0; + spi_dq1_d = 1'b0; + spi_dq2_d = 1'b0; + spi_dq3_d = 1'b0; + spi_dq0_oe_d = 1'b0; + spi_dq1_oe_d = 1'b0; + spi_dq2_oe_d = 1'b0; + spi_dq3_oe_d = 1'b0; + + case (spi_mode_q) + MODE_STAND_SPI: begin + if (data_width_q == 2'd1) begin + spi_dq0_d = out_data_d[15]; + end else if (data_width_q == 2'd2) begin + spi_dq0_d = out_data_d[31]; + end else begin + spi_dq0_d = out_data_d[7]; + end + spi_dq0_oe_d = 1'b1; + end + + MODE_DUAL_SPI: begin + if (data_width_q == 2'd1) begin + spi_dq0_d = out_data_d[14]; + spi_dq1_d = out_data_d[15]; + end else if (data_width_q == 2'd2) begin + spi_dq0_d = out_data_d[30]; + spi_dq1_d = out_data_d[31]; + end else begin + spi_dq0_d = out_data_d[6]; + spi_dq1_d = out_data_d[7]; + end + if (read_q) begin + spi_dq0_oe_d = 1'b0; + spi_dq1_oe_d = 1'b0; + end else begin + spi_dq0_oe_d = 1'b1; + spi_dq1_oe_d = 1'b1; + end + end + + MODE_QUAD_SPI: begin + if (data_width_q == 2'd1) begin + spi_dq0_d = out_data_d[12]; + spi_dq1_d = out_data_d[13]; + spi_dq2_d = out_data_d[14]; + spi_dq3_d = out_data_d[15]; + end else if (data_width_q == 2'd2) begin + spi_dq0_d = out_data_d[28]; + spi_dq1_d = out_data_d[29]; + spi_dq2_d = out_data_d[30]; + spi_dq3_d = out_data_d[31]; + end else begin + spi_dq0_d = out_data_d[4]; + spi_dq1_d = out_data_d[5]; + spi_dq2_d = out_data_d[6]; + spi_dq3_d = out_data_d[7]; + end + if (read_q) begin + spi_dq0_oe_d = 1'b0; + spi_dq1_oe_d = 1'b0; + spi_dq2_oe_d = 1'b0; + spi_dq3_oe_d = 1'b0; + end else begin + spi_dq0_oe_d = 1'b1; + spi_dq1_oe_d = 1'b1; + spi_dq2_oe_d = 1'b1; + spi_dq3_oe_d = 1'b1; + end + end + + default: ; + endcase + end + + assign spi_clk_oe_d = 1'b1; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + spi_clk_q <= '0; + spi_clk_oe_q <= '0; + spi_dq0_q <= '0; + spi_dq0_oe_q <= '0; + spi_dq1_q <= '0; + spi_dq1_oe_q <= '0; + spi_dq2_q <= '0; + spi_dq2_oe_q <= '0; + spi_dq3_q <= '0; + spi_dq3_oe_q <= '0; + end else begin + spi_clk_q <= spi_clk_d; + spi_clk_oe_q <= spi_clk_oe_d; + spi_dq0_q <= spi_dq0_d; + spi_dq0_oe_q <= spi_dq0_oe_d; + spi_dq1_q <= spi_dq1_d; + spi_dq1_oe_q <= spi_dq1_oe_d; + spi_dq2_q <= spi_dq2_d; + spi_dq2_oe_q <= spi_dq2_oe_d; + spi_dq3_q <= spi_dq3_d; + spi_dq3_oe_q <= spi_dq3_oe_d; + end + end + + assign data_o = in_data_q; + assign data_valid_o = data_valid_q; + assign idle_o = ready_q; + + assign spi_clk_o = spi_clk_q; + assign spi_clk_oe_o = spi_clk_oe_q; + assign spi_dq0_o = spi_dq0_q; + assign spi_dq0_oe_o = spi_dq0_oe_q; + assign spi_dq1_o = spi_dq1_q; + assign spi_dq1_oe_o = spi_dq1_oe_q; + assign spi_dq2_o = spi_dq2_q; + assign spi_dq2_oe_o = spi_dq2_oe_q; + assign spi_dq3_o = spi_dq3_q; + assign spi_dq3_oe_o = spi_dq3_oe_q; + + logic [15:0] ratio = 1 << div_ratio_q; + + clk_div #( + .RATIO_WIDTH(16) + ) u_clk_div ( + .clk_i(clk_i), + .rst_ni(rst_ni || (~((state_q == S_IDLE) && start_i))), + .en_i(state_q != S_IDLE), + .ratio_i(ratio), + .clk_o(tick) + ); + +endmodule diff --git a/rtl/perips/xip/xip_core.sv b/rtl/perips/xip/xip_core.sv new file mode 100644 index 0000000..8e736e8 --- /dev/null +++ b/rtl/perips/xip/xip_core.sv @@ -0,0 +1,143 @@ + /* + Copyright 2023 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 xip_core #( + parameter int unsigned TX_FIFO_DEPTH = 8, + parameter int unsigned RX_FIFO_DEPTH = 8 + )( + input logic clk_i, + input logic rst_ni, + + // SPI引脚信号 + input logic spi_clk_i, + output logic spi_clk_o, + output logic spi_clk_oe_o, + input logic spi_ss_i, + 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 + ); + + localparam OP_READ = 2'b00; + localparam OP_WRITE = 2'b01; + localparam OP_SECTOR_ERASE = 2'b10; + + localparam S_IDLE = 2'b01; + localparam S_WAIT = 2'b10; + + logic[1:0] state_d, state_q; + logic valid_d, valid_q; + logic[31:0] rdata_d, rdata_q; + logic[31:0] addr; + logic[1:0] op; + logic[31:0] rdata; + logic valid; + + // 去掉基地址 + assign addr = {9'h0, addr_i[22:0]}; + // addr_i[23], 1: 表示擦除扇区; 0: 表示编程数据 + assign op = (!we_i) ? OP_READ : addr_i[23] ? OP_SECTOR_ERASE : OP_WRITE; + + assign gnt_o = (req_i & (state_q == S_IDLE)); + assign rvalid_o = valid_q; + assign data_o = rdata_q; + + always_comb begin + state_d = state_q; + valid_d = valid_q; + rdata_d = rdata_q; + + case (state_q) + S_IDLE: begin + valid_d = 1'b0; + if (req_i) begin + state_d = S_WAIT; + end + end + + S_WAIT: begin + if (valid) begin + state_d = S_IDLE; + valid_d = 1'b1; + rdata_d = rdata; + end + end + + default: ; + endcase + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + state_q <= S_IDLE; + valid_q <= '0; + rdata_q <= '0; + end else begin + state_q <= state_d; + valid_q <= valid_d; + rdata_q <= rdata_d; + end + end + + xip_w25q64_ctrl u_flash( + .clk_i, + .rst_ni, + .req_i, + .op_i (op), + .addr_i (addr), + .wdata_i(data_i), + .valid_o(valid), + .rdata_o(rdata), + .spi_clk_o, + .spi_clk_oe_o, + .spi_ss_o, + .spi_ss_oe_o, + .spi_dq0_i, + .spi_dq0_o, + .spi_dq0_oe_o, + .spi_dq1_i, + .spi_dq1_o, + .spi_dq1_oe_o, + .spi_dq2_i, + .spi_dq2_o, + .spi_dq2_oe_o, + .spi_dq3_i, + .spi_dq3_o, + .spi_dq3_oe_o + ); + +endmodule diff --git a/rtl/perips/xip/xip_top.sv b/rtl/perips/xip/xip_top.sv new file mode 100644 index 0000000..03d1f04 --- /dev/null +++ b/rtl/perips/xip/xip_top.sv @@ -0,0 +1,81 @@ + /* + Copyright 2023 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 xip_top ( + input logic clk_i, + input logic rst_ni, + + // SPI引脚信号 + input logic spi_clk_i, + output logic spi_clk_o, + output logic spi_clk_oe_o, + input logic spi_ss_i, + 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 + ); + + xip_core u_xip_core ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .spi_clk_o (spi_clk_o), + .spi_clk_oe_o (spi_clk_oe_o), + .spi_ss_o (spi_ss_o), + .spi_ss_oe_o (spi_ss_oe_o), + .spi_dq0_i (spi_dq0_i), + .spi_dq0_o (spi_dq0_o), + .spi_dq0_oe_o (spi_dq0_oe_o), + .spi_dq1_i (spi_dq1_i), + .spi_dq1_o (spi_dq1_o), + .spi_dq1_oe_o (spi_dq1_oe_o), + .spi_dq2_i (spi_dq2_i), + .spi_dq2_o (spi_dq2_o), + .spi_dq2_oe_o (spi_dq2_oe_o), + .spi_dq3_i (spi_dq3_i), + .spi_dq3_o (spi_dq3_o), + .spi_dq3_oe_o (spi_dq3_oe_o), + .req_i (req_i), + .we_i (we_i), + .be_i (be_i), + .addr_i (addr_i), + .data_i (data_i), + .gnt_o (gnt_o), + .rvalid_o (rvalid_o), + .data_o (data_o) + ); + +endmodule diff --git a/rtl/perips/xip/xip_w25q64_ctrl.sv b/rtl/perips/xip/xip_w25q64_ctrl.sv new file mode 100644 index 0000000..bb6b85e --- /dev/null +++ b/rtl/perips/xip/xip_w25q64_ctrl.sv @@ -0,0 +1,316 @@ + /* + Copyright 2023 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 xip_w25q64_ctrl( + input logic clk_i, + input logic rst_ni, + + input logic req_i, + input logic [1:0] op_i, + input logic [31:0] addr_i, + input logic [31:0] wdata_i, + output logic valid_o, + output logic [31:0] rdata_o, + + 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 + ); + + // SPI模式 + localparam MODE_STAND_SPI = 2'b00; + localparam MODE_DUAL_SPI = 2'b01; + localparam MODE_QUAD_SPI = 2'b10; + // 数据宽度 + localparam SPI_DATA_WIDTH_8 = 2'b00; + localparam SPI_DATA_WIDTH_16 = 2'b01; + localparam SPI_DATA_WIDTH_32 = 2'b10; + // 2分频 + localparam SPI_CLK_DIV = 3'd1; + // SPI极性 + localparam SPI_CPOL_CPHA = 2'b00; + + localparam OP_READ = 2'b00; + localparam OP_WRITE = 2'b01; + localparam OP_SECTOR_ERASE = 2'b10; + + localparam STATE_NUM = 12; + localparam S_IDLE = 12'h001; + localparam S_SS_LOW = 12'h002; + localparam S_SS_HIGH = 12'h004; + localparam S_WRITE_ENABLE = 12'h008; + localparam S_WRITE_DISABLE = 12'h010; + localparam S_SECTOR_ERASE = 12'h020; + localparam S_PAGE_PROGRAM = 12'h040; + localparam S_WRITE_DATA = 12'h080; + localparam S_READ_DATA = 12'h100; + localparam S_READ = 12'h200; + localparam S_READ_STATUS = 12'h400; + localparam S_CHECK_WIP = 12'h800; + + logic [STATE_NUM-1:0] state_d, state_q; + logic [STATE_NUM-1:0] next_state_d, next_state_q; + logic [31:0] addr_d, addr_q; + logic [31:0] wdata_d, wdata_q; + logic [1:0] op_d, op_q; + logic spi_ss_o_d, spi_ss_o_q; + + logic start_d; + logic read_d; + logic [1:0] spi_mode_d; + logic [1:0] cp_mode_d; + logic [1:0] data_width_d; + logic [31:0] data_d; + logic [2:0] div_ratio_d; + logic msb_first_d; + + logic spi_idle; + logic spi_valid; + logic [31:0] spi_data_out; + + always_comb begin + state_d = state_q; + next_state_d = next_state_q; + addr_d = addr_q; + wdata_d = wdata_q; + op_d = op_q; + spi_ss_o_d = spi_ss_o_q; + + start_d = '0; + read_d = '0; + spi_mode_d = '0; + data_width_d = '0; + data_d = '0; + cp_mode_d = SPI_CPOL_CPHA; + div_ratio_d = SPI_CLK_DIV; + msb_first_d = 1'b1; + + case (state_q) + S_IDLE: begin + spi_ss_o_d = 1'b1; + if (req_i) begin + addr_d = addr_i; + op_d = op_i; + wdata_d = wdata_i; + state_d = S_SS_LOW; + if ((op_i == OP_WRITE) | (op_i == OP_SECTOR_ERASE)) begin + next_state_d = S_WRITE_ENABLE; + end else begin + next_state_d = S_READ_DATA; + end + end + end + + S_SS_LOW: begin + if (spi_idle) begin + spi_ss_o_d = 1'b0; + state_d = next_state_q; + end + end + + S_WRITE_ENABLE: begin + if (spi_idle) begin + start_d = 1'b1; + read_d = 1'b0; + spi_mode_d = MODE_STAND_SPI; + data_width_d = SPI_DATA_WIDTH_8; + data_d = 8'h06; + if (op_q == OP_SECTOR_ERASE) begin + next_state_d = S_SECTOR_ERASE; + end else begin + next_state_d = S_PAGE_PROGRAM; + end + state_d = S_SS_HIGH; + end + end + + S_WRITE_DISABLE: begin + if (spi_idle) begin + start_d = 1'b1; + read_d = 1'b0; + spi_mode_d = MODE_STAND_SPI; + data_width_d = SPI_DATA_WIDTH_8; + data_d = 8'h04; + state_d = S_SS_HIGH; + next_state_d = S_IDLE; + end + end + + S_PAGE_PROGRAM: begin + if (spi_idle) begin + start_d = 1'b1; + read_d = 1'b0; + spi_mode_d = MODE_STAND_SPI; + data_width_d = SPI_DATA_WIDTH_32; + data_d = {8'h02, addr_q[23:0]}; + state_d = S_WRITE_DATA; + end + end + + S_WRITE_DATA: begin + if (spi_idle) begin + start_d = 1'b1; + read_d = 1'b0; + spi_mode_d = MODE_STAND_SPI; + data_width_d = SPI_DATA_WIDTH_32; + data_d = wdata_q; + state_d = S_SS_HIGH; + next_state_d = S_READ_STATUS; + end + end + + S_SECTOR_ERASE: begin + if (spi_idle) begin + start_d = 1'b1; + read_d = 1'b0; + spi_mode_d = MODE_STAND_SPI; + data_width_d = SPI_DATA_WIDTH_32; + data_d = {8'h20, addr_q[23:0]}; + state_d = S_SS_HIGH; + next_state_d = S_READ_STATUS; + end + end + + S_READ_STATUS: begin + if (spi_idle) begin + start_d = 1'b1; + read_d = 1'b0; + spi_mode_d = MODE_STAND_SPI; + data_width_d = SPI_DATA_WIDTH_8; + data_d = 8'h05; + state_d = S_READ; + next_state_d = S_CHECK_WIP; + end + end + + S_CHECK_WIP: begin + if (spi_idle) begin + // flash is in WIP + if (spi_data_out[0]) begin + state_d = S_SS_HIGH; + next_state_d = S_READ_STATUS; + end else begin + state_d = S_SS_HIGH; + state_d = S_WRITE_DISABLE; + end + end + end + + S_READ_DATA: begin + if (spi_idle) begin + start_d = 1'b1; + read_d = 1'b0; + spi_mode_d = MODE_STAND_SPI; + data_width_d = SPI_DATA_WIDTH_32; + data_d = {8'h03, addr_q[23:0]}; + state_d = S_READ; + next_state_d = S_IDLE; + end + end + + S_READ: begin + if (spi_idle) begin + start_d = 1'b1; + read_d = 1'b1; + spi_mode_d = MODE_STAND_SPI; + data_width_d = SPI_DATA_WIDTH_32; + state_d = S_SS_HIGH; + end + end + + S_SS_HIGH: begin + if (spi_idle) begin + spi_ss_o_d = 1'b1; + if (next_state_q != S_IDLE) begin + state_d = S_SS_LOW; + end else begin + state_d = S_IDLE; + end + end + end + + default: ; + endcase + end + + assign valid_o = (state_q == S_SS_HIGH) & spi_idle & (next_state_q == S_IDLE); + assign rdata_o = spi_data_out; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + state_q <= S_IDLE; + next_state_q <= S_IDLE; + addr_q <= '0; + wdata_q <= '0; + op_q <= '0; + spi_ss_o_q <= '0; + end else begin + state_q <= state_d; + next_state_q <= next_state_d; + addr_q <= addr_d; + wdata_q <= wdata_d; + op_q <= op_d; + spi_ss_o_q <= spi_ss_o_d; + end + end + + assign spi_ss_oe_o = 1'b1; + assign spi_ss_o = spi_ss_o_q; + + spi_master_transmit u_spi_master ( + .clk_i, + .rst_ni, + .start_i (start_d), + .read_i (read_d), + .spi_mode_i (spi_mode_d), + .cp_mode_i (cp_mode_d), + .data_width_i(data_width_d), + .data_i (data_d), + .div_ratio_i (div_ratio_d), + .msb_first_i (msb_first_d), + .data_o (spi_data_out), + .idle_o (spi_idle), + .data_valid_o(spi_valid), + .spi_clk_o, + .spi_clk_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