// // Fast data download from 2-bit SPI flash, or zero SDRAM. // // Feed a FIFO that then writes to SDRAM. // Requires writes in aligned 8-byte chunks. // // This unit does *not* require a 2x SPI clock; // it uses a DDR buffer for clock out. // module spirom ( input rst_n, input rom_clk, input ram_clk, input sys_clk, /* SPI ROM interface */ output spi_sck, inout [1:0] spi_io, output reg spi_cs_n, /* SDRAM interface */ output [15:0] wd, // Data to RAM (* syn_preserve = 1 *) // Don't merge into FIFO output [24:1] waddr, // RAM address output reg [1:0] wrq, // Write request (min 4/8 bytes) input wacc, // Data accepted (ready for next data) /* CPU control interface */ output [31:0] cpu_rdata, input [31:0] cpu_wdata, input cpu_valid, input [3:0] cpu_wstrb, input [1:0] cpu_addr, output reg irq ); reg [24:3] ramstart; reg [23:3] romstart; reg [23:3] datalen; reg is_spi; reg go_zero; reg go_spi; reg done; reg [1:0] done_q; always @(negedge rst_n or posedge sys_clk) if (~rst_n) begin ramstart <= 23'bx; romstart <= 23'bx; datalen <= 21'bx; is_spi <= 1'b0; go_zero <= 1'b0; go_spi <= 1'b0; done_q <= 2'b11; irq <= 1'b1; end else begin done_q <= { done_q[0], done }; if (cpu_valid & cpu_wstrb[0]) begin // Only full word writes supported!! case (cpu_addr) 2'b00: begin ramstart <= cpu_wdata[24:3]; end 2'b01: begin romstart <= cpu_wdata[23:3]; is_spi <= |cpu_wdata[23:3]; end 2'b10: begin datalen <= cpu_wdata[23:3]; if (|cpu_wdata[23:3]) begin go_zero <= ~is_spi; go_spi <= is_spi; irq <= 1'b0; end end default: begin // Do nothing end endcase // case (cpu_addr) end // if (cpu_valid & cpu_wstrb[0]) else if (done_q == 2'b01) begin go_zero <= 1'b0; go_spi <= 1'b0; irq <= 1'b1; end end // else: !if(~rst_n) always_comb case (cpu_addr) 2'b00: cpu_rdata = { 7'b0, ramstart, 3'b0 }; 2'b01: cpu_rdata = { 8'b0, romstart, 3'b0 }; 2'b10: cpu_rdata = { 8'b0, datalen, 3'b0 }; 2'b11: cpu_rdata = { 31'b0, irq }; endcase // case (cpu_addr) // // FIFO and input latches // reg [1:0] spi_in_q; reg spi_in_req; reg spi_in_req_q; wire [11:0] wrusedw; wire [8:0] rdusedw; wire [15:0] fifo_out; ddufifo spirom_fifo ( .aclr ( ~rst_n ), .wrclk ( rom_clk ), .data ( spi_in_q ), .wrreq ( spi_in_req_q ), .wrusedw ( wrusedw ), .rdclk ( ram_clk ), .q ( fifo_out ), .rdreq ( wacc & go_spi ), .rdusedw ( rdusedw ) ); // // Interfacing between FIFO and input signals // // Shuffle fifo_out because SPI brings in data in bigendian bit // order within bytes, but the FIFO IP assumes littleendian // assign wd[ 7: 6] = {2{go_spi}} & fifo_out[ 1: 0]; assign wd[ 5: 4] = {2{go_spi}} & fifo_out[ 3: 2]; assign wd[ 3: 2] = {2{go_spi}} & fifo_out[ 5: 4]; assign wd[ 1: 0] = {2{go_spi}} & fifo_out[ 7: 6]; assign wd[15:14] = {2{go_spi}} & fifo_out[ 9: 8]; assign wd[13:12] = {2{go_spi}} & fifo_out[11:10]; assign wd[11:10] = {2{go_spi}} & fifo_out[13:12]; assign wd[ 9: 8] = {2{go_spi}} & fifo_out[15:14]; reg [24:1] waddr_q; reg [23:1] ram_data_ctr; reg wacc_q; reg [1:0] go_ram_q; assign waddr = waddr_q; always @(negedge rst_n or posedge ram_clk) if (~rst_n) begin waddr_q <= 24'bx; ram_data_ctr <= 23'bx; wacc_q <= 1'b0; done <= 1'b1; go_ram_q <= 2'b00; wrq <= 2'b00; end else begin wrq <= 2'b00; if (go_spi & ~done) begin wrq[0] <= rdusedw >= 9'd4; // 4*2 = 8 bytes min available wrq[1] <= rdusedw >= 9'd8; // 8*2 = 16 bytes min available end else if (go_zero & ~done) begin wrq[0] <= |ram_data_ctr[23:3]; wrq[1] <= |ram_data_ctr[23:4]; end wacc_q <= wacc; go_ram_q <= { go_ram_q[0], go_spi|go_zero }; if (go_ram_q == 2'b01) begin waddr_q <= {ramstart, 2'b00}; ram_data_ctr <= { datalen, 2'b00}; done <= 1'b0; end else if (~done) begin waddr_q <= waddr_q + wacc_q; ram_data_ctr <= ram_data_ctr - wacc_q; done <= !(ram_data_ctr - wacc_q); end end // else: !if(~rst_n) reg [5:0] spi_cmd_ctr; reg [23:-2] spi_data_ctr; reg spi_clk_en = 1'b0; reg spi_mosi_en; reg [1:0] go_spi_q; wire go_spi_s; // Explicit synchronizer for go_spi synchronizer #(.width(1)) go_spi_synchro ( .rst_n ( rst_n ), .clk ( rom_clk ), .d ( go_spi ), .q ( go_spi_s ) ); always @(negedge rst_n or posedge rom_clk) if (~rst_n) begin spi_cmd_ctr <= 6'b0; spi_clk_en <= 1'b0; spi_data_ctr <= 26'b0; spi_cs_n <= 1'b1; spi_in_req <= 1'b0; spi_in_req_q <= 1'b0; spi_mosi_en <= 1'b1; go_spi_q <= 4'b0000; end else begin go_spi_q <= { go_spi_q[0], go_spi_s }; spi_in_q <= spi_io; spi_in_req <= 1'b0; spi_in_req_q <= spi_in_req; spi_clk_en <= 1'b0; spi_cs_n <= 1'b1; // Note: datalen <- spi_data_ctr is a 2-cycle multipath if (go_spi_q == 2'b01) begin spi_data_ctr <= { datalen, 5'b0 }; spi_cmd_ctr <= 6'b0; spi_cs_n <= 1'b0; end if ( ~|spi_data_ctr | ~go_spi_q[1] ) begin spi_clk_en <= 1'b0; spi_mosi_en <= 1'b1; end else begin spi_cs_n <= 1'b0; if ( ~spi_cs_n ) begin // 64/4 = 16 bytes min space spi_clk_en <= (~wrusedw) >= 12'd128; if ( spi_clk_en ) begin if ( spi_cmd_ctr[5] & spi_cmd_ctr[2] ) spi_mosi_en <= 1'b0; if ( spi_cmd_ctr[5] & spi_cmd_ctr[3] ) begin spi_in_req <= 1'b1; spi_data_ctr <= spi_data_ctr - 1'b1; end else begin spi_cmd_ctr <= spi_cmd_ctr + 1'b1; end end // if ( spi_clk_en ) end // if ( ~spi_cs_n ) end // else: !if( ~|spi_data_ctr ) end // else: !if(~rst_n) // SPI output data is shifted on the negative edge reg [31:0] spi_cmd; reg spi_clk_en_q; assign spi_io[0] = spi_mosi_en ? spi_cmd[31] : 1'bz; assign spi_io[1] = 1'bz; always @(negedge rst_n or negedge rom_clk) if (~rst_n) begin spi_cmd <= 32'bx; // Fast Read Dual Output spi_clk_en_q <= 1'b0; end else begin if (spi_cs_n) begin spi_cmd[31:24] <= 8'h3b; // Fast Read Dual Output spi_cmd[23: 0] <= { romstart, 3'b000 }; // Byte address end spi_clk_en_q <= spi_clk_en; if ( spi_clk_en_q ) spi_cmd <= (spi_cmd << 1) | 1'b1; end // // SPI_SCK output buffer // ddio_out spi_clk_buf ( .aclr ( ~rst_n ), .datain_h ( spi_clk_en_q ), .datain_l ( 1'b0 ), .outclock ( rom_clk ), .dataout ( spi_sck ) ); endmodule // spirom