123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- //
- // 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
|