/* * PicoSoC - A simple example SoC using PicoRV32 * * Copyright (C) 2017 Clifford Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ module spimemio ( input clk, resetn, input valid, output ready, input [23:0] addr, output reg [31:0] rdata, output flash_csb, output flash_clk, output flash_io0_oe, output flash_io1_oe, output flash_io2_oe, output flash_io3_oe, output flash_io0_do, output flash_io1_do, output flash_io2_do, output flash_io3_do, input flash_io0_di, input flash_io1_di, input flash_io2_di, input flash_io3_di, input [3:0] cfgreg_we, input [31:0] cfgreg_di, output [31:0] cfgreg_do ); reg xfer_resetn; reg din_valid; wire din_ready; reg [7:0] din_data; reg [3:0] din_tag; reg din_cont; reg din_qspi; reg din_ddr; reg din_rd; wire dout_valid; wire [7:0] dout_data; wire [3:0] dout_tag; reg [23:0] buffer; reg [23:0] rd_addr; reg rd_valid; reg rd_wait; reg rd_inc; assign ready = valid && (addr == rd_addr) && rd_valid; wire jump = valid && !ready && (addr != rd_addr+4) && rd_valid; reg softreset; reg config_en; // cfgreg[31] reg config_ddr; // cfgreg[22] reg config_qspi; // cfgreg[21] reg config_cont; // cfgreg[20] reg [3:0] config_dummy; // cfgreg[19:16] reg [3:0] config_oe; // cfgreg[11:8] reg config_csb; // cfgreg[5] reg config_clk; // cfgref[4] reg [3:0] config_do; // cfgreg[3:0] assign cfgreg_do[31] = config_en; assign cfgreg_do[30:23] = 0; assign cfgreg_do[22] = config_ddr; assign cfgreg_do[21] = config_qspi; assign cfgreg_do[20] = config_cont; assign cfgreg_do[19:16] = config_dummy; assign cfgreg_do[15:12] = 0; assign cfgreg_do[11:8] = {flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}; assign cfgreg_do[7:6] = 0; assign cfgreg_do[5] = flash_csb; assign cfgreg_do[4] = flash_clk; assign cfgreg_do[3:0] = {flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}; always @(posedge clk) begin softreset <= !config_en || cfgreg_we; if (!resetn) begin softreset <= 1; config_en <= 1; config_csb <= 0; config_clk <= 0; config_oe <= 0; config_do <= 0; config_ddr <= 0; config_qspi <= 0; config_cont <= 0; config_dummy <= 8; end else begin if (cfgreg_we[0]) begin config_csb <= cfgreg_di[5]; config_clk <= cfgreg_di[4]; config_do <= cfgreg_di[3:0]; end if (cfgreg_we[1]) begin config_oe <= cfgreg_di[11:8]; end if (cfgreg_we[2]) begin config_ddr <= cfgreg_di[22]; config_qspi <= cfgreg_di[21]; config_cont <= cfgreg_di[20]; config_dummy <= cfgreg_di[19:16]; end if (cfgreg_we[3]) begin config_en <= cfgreg_di[31]; end end end wire xfer_csb; wire xfer_clk; wire xfer_io0_oe; wire xfer_io1_oe; wire xfer_io2_oe; wire xfer_io3_oe; wire xfer_io0_do; wire xfer_io1_do; wire xfer_io2_do; wire xfer_io3_do; reg xfer_io0_90; reg xfer_io1_90; reg xfer_io2_90; reg xfer_io3_90; always @(negedge clk) begin xfer_io0_90 <= xfer_io0_do; xfer_io1_90 <= xfer_io1_do; xfer_io2_90 <= xfer_io2_do; xfer_io3_90 <= xfer_io3_do; end assign flash_csb = config_en ? xfer_csb : config_csb; assign flash_clk = config_en ? xfer_clk : config_clk; assign flash_io0_oe = config_en ? xfer_io0_oe : config_oe[0]; assign flash_io1_oe = config_en ? xfer_io1_oe : config_oe[1]; assign flash_io2_oe = config_en ? xfer_io2_oe : config_oe[2]; assign flash_io3_oe = config_en ? xfer_io3_oe : config_oe[3]; assign flash_io0_do = config_en ? (config_ddr ? xfer_io0_90 : xfer_io0_do) : config_do[0]; assign flash_io1_do = config_en ? (config_ddr ? xfer_io1_90 : xfer_io1_do) : config_do[1]; assign flash_io2_do = config_en ? (config_ddr ? xfer_io2_90 : xfer_io2_do) : config_do[2]; assign flash_io3_do = config_en ? (config_ddr ? xfer_io3_90 : xfer_io3_do) : config_do[3]; wire xfer_dspi = din_ddr && !din_qspi; wire xfer_ddr = din_ddr && din_qspi; spimemio_xfer xfer ( .clk (clk ), .resetn (xfer_resetn ), .din_valid (din_valid ), .din_ready (din_ready ), .din_data (din_data ), .din_tag (din_tag ), .din_cont (din_cont ), .din_dspi (xfer_dspi ), .din_qspi (din_qspi ), .din_ddr (xfer_ddr ), .din_rd (din_rd ), .dout_valid (dout_valid ), .dout_data (dout_data ), .dout_tag (dout_tag ), .flash_csb (xfer_csb ), .flash_clk (xfer_clk ), .flash_io0_oe (xfer_io0_oe ), .flash_io1_oe (xfer_io1_oe ), .flash_io2_oe (xfer_io2_oe ), .flash_io3_oe (xfer_io3_oe ), .flash_io0_do (xfer_io0_do ), .flash_io1_do (xfer_io1_do ), .flash_io2_do (xfer_io2_do ), .flash_io3_do (xfer_io3_do ), .flash_io0_di (flash_io0_di), .flash_io1_di (flash_io1_di), .flash_io2_di (flash_io2_di), .flash_io3_di (flash_io3_di) ); reg [3:0] state; always @(posedge clk) begin xfer_resetn <= 1; din_valid <= 0; if (!resetn || softreset) begin state <= 0; xfer_resetn <= 0; rd_valid <= 0; din_tag <= 0; din_cont <= 0; din_qspi <= 0; din_ddr <= 0; din_rd <= 0; end else begin if (dout_valid && dout_tag == 1) buffer[ 7: 0] <= dout_data; if (dout_valid && dout_tag == 2) buffer[15: 8] <= dout_data; if (dout_valid && dout_tag == 3) buffer[23:16] <= dout_data; if (dout_valid && dout_tag == 4) begin rdata <= {dout_data, buffer}; rd_addr <= rd_inc ? rd_addr + 4 : addr; rd_valid <= 1; rd_wait <= rd_inc; rd_inc <= 1; end if (valid) rd_wait <= 0; case (state) 0: begin din_valid <= 1; din_data <= 8'h ff; din_tag <= 0; if (din_ready) begin din_valid <= 0; state <= 1; end end 1: begin if (dout_valid) begin xfer_resetn <= 0; state <= 2; end end 2: begin din_valid <= 1; din_data <= 8'h ab; din_tag <= 0; if (din_ready) begin din_valid <= 0; state <= 3; end end 3: begin if (dout_valid) begin xfer_resetn <= 0; state <= 4; end end 4: begin rd_inc <= 0; din_valid <= 1; din_tag <= 0; case ({config_ddr, config_qspi}) 2'b11: din_data <= 8'h ED; 2'b01: din_data <= 8'h EB; 2'b10: din_data <= 8'h BB; 2'b00: din_data <= 8'h 03; endcase if (din_ready) begin din_valid <= 0; state <= 5; end end 5: begin if (valid && !ready) begin din_valid <= 1; din_tag <= 0; din_data <= addr[23:16]; din_qspi <= config_qspi; din_ddr <= config_ddr; if (din_ready) begin din_valid <= 0; state <= 6; end end end 6: begin din_valid <= 1; din_tag <= 0; din_data <= addr[15:8]; if (din_ready) begin din_valid <= 0; state <= 7; end end 7: begin din_valid <= 1; din_tag <= 0; din_data <= addr[7:0]; if (din_ready) begin din_valid <= 0; din_data <= 0; state <= config_qspi || config_ddr ? 8 : 9; end end 8: begin din_valid <= 1; din_tag <= 0; din_data <= config_cont ? 8'h A5 : 8'h FF; if (din_ready) begin din_rd <= 1; din_data <= config_dummy; din_valid <= 0; state <= 9; end end 9: begin din_valid <= 1; din_tag <= 1; if (din_ready) begin din_valid <= 0; state <= 10; end end 10: begin din_valid <= 1; din_data <= 8'h 00; din_tag <= 2; if (din_ready) begin din_valid <= 0; state <= 11; end end 11: begin din_valid <= 1; din_tag <= 3; if (din_ready) begin din_valid <= 0; state <= 12; end end 12: begin if (!rd_wait || valid) begin din_valid <= 1; din_tag <= 4; if (din_ready) begin din_valid <= 0; state <= 9; end end end endcase if (jump) begin rd_inc <= 0; rd_valid <= 0; xfer_resetn <= 0; if (config_cont) begin state <= 5; end else begin state <= 4; din_qspi <= 0; din_ddr <= 0; end din_rd <= 0; end end end endmodule module spimemio_xfer ( input clk, resetn, input din_valid, output din_ready, input [7:0] din_data, input [3:0] din_tag, input din_cont, input din_dspi, input din_qspi, input din_ddr, input din_rd, output dout_valid, output [7:0] dout_data, output [3:0] dout_tag, output reg flash_csb, output reg flash_clk, output reg flash_io0_oe, output reg flash_io1_oe, output reg flash_io2_oe, output reg flash_io3_oe, output reg flash_io0_do, output reg flash_io1_do, output reg flash_io2_do, output reg flash_io3_do, input flash_io0_di, input flash_io1_di, input flash_io2_di, input flash_io3_di ); reg [7:0] obuffer; reg [7:0] ibuffer; reg [3:0] count; reg [3:0] dummy_count; reg xfer_cont; reg xfer_dspi; reg xfer_qspi; reg xfer_ddr; reg xfer_ddr_q; reg xfer_rd; reg [3:0] xfer_tag; reg [3:0] xfer_tag_q; reg [7:0] next_obuffer; reg [7:0] next_ibuffer; reg [3:0] next_count; reg fetch; reg next_fetch; reg last_fetch; always @(posedge clk) begin xfer_ddr_q <= xfer_ddr; xfer_tag_q <= xfer_tag; end assign din_ready = din_valid && resetn && next_fetch; assign dout_valid = (xfer_ddr_q ? fetch && !last_fetch : next_fetch && !fetch) && resetn; assign dout_data = ibuffer; assign dout_tag = xfer_tag_q; always @* begin flash_io0_oe = 0; flash_io1_oe = 0; flash_io2_oe = 0; flash_io3_oe = 0; flash_io0_do = 0; flash_io1_do = 0; flash_io2_do = 0; flash_io3_do = 0; next_obuffer = obuffer; next_ibuffer = ibuffer; next_count = count; next_fetch = 0; if (dummy_count == 0) begin casez ({xfer_ddr, xfer_qspi, xfer_dspi}) 3'b 000: begin flash_io0_oe = 1; flash_io0_do = obuffer[7]; if (flash_clk) begin next_obuffer = {obuffer[6:0], 1'b 0}; next_count = count - |count; end else begin next_ibuffer = {ibuffer[6:0], flash_io1_di}; end next_fetch = (next_count == 0); end 3'b 01?: begin flash_io0_oe = !xfer_rd; flash_io1_oe = !xfer_rd; flash_io2_oe = !xfer_rd; flash_io3_oe = !xfer_rd; flash_io0_do = obuffer[4]; flash_io1_do = obuffer[5]; flash_io2_do = obuffer[6]; flash_io3_do = obuffer[7]; if (flash_clk) begin next_obuffer = {obuffer[3:0], 4'b 0000}; next_count = count - {|count, 2'b00}; end else begin next_ibuffer = {ibuffer[3:0], flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}; end next_fetch = (next_count == 0); end 3'b 11?: begin flash_io0_oe = !xfer_rd; flash_io1_oe = !xfer_rd; flash_io2_oe = !xfer_rd; flash_io3_oe = !xfer_rd; flash_io0_do = obuffer[4]; flash_io1_do = obuffer[5]; flash_io2_do = obuffer[6]; flash_io3_do = obuffer[7]; next_obuffer = {obuffer[3:0], 4'b 0000}; next_ibuffer = {ibuffer[3:0], flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}; next_count = count - {|count, 2'b00}; next_fetch = (next_count == 0); end 3'b ??1: begin flash_io0_oe = !xfer_rd; flash_io1_oe = !xfer_rd; flash_io0_do = obuffer[6]; flash_io1_do = obuffer[7]; if (flash_clk) begin next_obuffer = {obuffer[5:0], 2'b 00}; next_count = count - {|count, 1'b0}; end else begin next_ibuffer = {ibuffer[5:0], flash_io1_di, flash_io0_di}; end next_fetch = (next_count == 0); end endcase end end always @(posedge clk) begin if (!resetn) begin fetch <= 1; last_fetch <= 1; flash_csb <= 1; flash_clk <= 0; count <= 0; dummy_count <= 0; xfer_tag <= 0; xfer_cont <= 0; xfer_dspi <= 0; xfer_qspi <= 0; xfer_ddr <= 0; xfer_rd <= 0; end else begin fetch <= next_fetch; last_fetch <= xfer_ddr ? fetch : 1; if (dummy_count) begin flash_clk <= !flash_clk && !flash_csb; dummy_count <= dummy_count - flash_clk; end else if (count) begin flash_clk <= !flash_clk && !flash_csb; obuffer <= next_obuffer; ibuffer <= next_ibuffer; count <= next_count; end if (din_valid && din_ready) begin flash_csb <= 0; flash_clk <= 0; count <= 8; dummy_count <= din_rd ? din_data : 0; obuffer <= din_data; xfer_tag <= din_tag; xfer_cont <= din_cont; xfer_dspi <= din_dspi; xfer_qspi <= din_qspi; xfer_ddr <= din_ddr; xfer_rd <= din_rd; end end end endmodule