URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [orpsocv2/] [boards/] [xilinx/] [atlys/] [rtl/] [verilog/] [xilinx_ddr2/] [xilinx_ddr2_if.v] - Rev 677
Compare with Previous | Blame | View Log
////////////////////////////////////////////////////////////////////// //// //// //// Xilinx Spartan-6 DDR2 controller Wishbone Interface //// //// //// //// Description //// //// Simple interface to the Xilinx MIG generated DDR2 controller//// //// //// //// To Do: //// //// Use full capacity of BRAM //// //// Employ LRU replacement scheme //// //// Remove hard-coding of things relating to number of lines //// //// //// //// Author(s): //// //// - Julius Baxter, julius.baxter@orsoc.se //// //// - Stefan Kristiansson, stefan.kristiansson@saunalahti.fi//// //// //// ////////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2010 Authors and OPENCORES.ORG //// //// //// //// This source file may be used and distributed without //// //// restriction provided that this copyright statement is not //// //// removed from the file and that any derivative work contains //// //// the original copyright notice and the associated disclaimer. //// //// //// //// This source file is free software; you can redistribute it //// //// and/or modify it under the terms of the GNU Lesser General //// //// Public License as published by the Free Software Foundation; //// //// either version 2.1 of the License, or (at your option) any //// //// later version. //// //// //// //// This source is distributed in the hope that it will be //// //// useful, but WITHOUT ANY WARRANTY; without even the implied //// //// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// //// PURPOSE. See the GNU Lesser General Public License for more //// //// details. //// //// //// //// You should have received a copy of the GNU Lesser General //// //// Public License along with this source; if not, download it //// //// from http://www.opencores.org/lgpl.shtml //// //// //// ////////////////////////////////////////////////////////////////////// /* * This is an interface to the Xilinx MIG-sourced DDR2 controller. * * The interface is based on the ML501 Virtex-5 board implementation * with the following adaptions to suite the Atlys Spartan-6 MIG and * DDR2 memory chip: * - Control and data FIFOs are clocked with the Wishbone bus clock * and not the DDR2 clock. * This way alot of clock domain crossing headaches can be avoided * (Virtex-5 MIG demands control and data FIFOs clocks to be * synchronous with the DDR2 clock, Spartan-6 have FIFOs that * are asynchronous to the DDR2 clock) * - The Atlys board have a DDR2 memory with a 16-bit data bus * apposed to the ML501 64-bit databus. This in combination * with changes in the MIG causes the addressing scheme to be a * bit different. A user port of 128-bit is being used, so * we are doing memory accesses on 128-bit boundaries * (4 address bits). * * See the Xilinx user guide UG388.pdf for more information on the * Spartan-6 FPGA memory controller * * * The controller's interface is via FIFO buffers - one for address and control * the other is for data. The data FIFO interface is 128-bits wide. * * This module has a cache with different aspects on each port. As we're to * ultimately interface to a 32-bit wide Wishbone bus, one side is 32-bits * and the other is 128-bits wide to accommodate the DDR2 controller's data * path. * * At present, the cache controller doesn't employ associativity, so any * line can be used for any location. A round-robin approach to line * use is employed. TODO is LRU scheme instead of round robin. * * The cache is a macro generated by Xilinx's IP generation tool. This is * because memories with dual-aspect ratios cannot be inferred via HDL. * * The size of lines, as set by the defines, controls how long each read * and write burst to/from the SDRAM is. * * The control and data FIFOS of the DDR2 interface are asynchronous to the DDR2 bus, * so they are clocked with the same clock as the Wisbone interface (i.e. wb_clk) */ module xilinx_ddr2_if ( input [31:0] wb_adr_i, input wb_stb_i, input wb_cyc_i, input [2:0] wb_cti_i, input [1:0] wb_bte_i, input wb_we_i, input [3:0] wb_sel_i, input [31:0] wb_dat_i, output [31:0] wb_dat_o, output reg wb_ack_o, output [12:0] ddr2_a, output [2:0] ddr2_ba, output ddr2_ras_n, output ddr2_cas_n, output ddr2_we_n, output ddr2_rzq, output ddr2_zio, output ddr2_odt, output ddr2_cke, output ddr2_dm, output ddr2_udm, inout [15:0] ddr2_dq, inout ddr2_dqs, inout ddr2_dqs_n, inout ddr2_udqs, inout ddr2_udqs_n, output ddr2_ck, output ddr2_ck_n, input ddr2_if_clk, input ddr2_if_rst, input idly_clk_100, input wb_clk, input wb_rst); `include "xilinx_ddr2_params.v" // Define to add a counter, signaling error if the controller locks up // (no ack after a certain period of time) //`define ERR_COUNTER /* `define DDR2_CACHE_NUM_LINES 16 `define DDR2_CACHE_NUM_LINES_ENC_WIDTH 4 // log2(`DDR2_CACHE_NUM_LINES) */ `define DDR2_CACHE_NUM_LINES 4 `define DDR2_CACHE_NUM_LINES_ENC_WIDTH 2 // log2(`DDR2_CACHE_NUM_LINES) `define DDR2_CACHE_NUM_WORDS_PER_LINE 256 `define DDR2_CACHE_ADDR_WIDTH_WORDS_PER_LINE 8 `define DDR2_CACHE_TAG_ADDR_WIDTH (32-`DDR2_CACHE_ADDR_WIDTH_WORDS_PER_LINE-2) `define DDR2_CACHE_DDR2_SIDE_NUM_WORDS_PER_LINE (`DDR2_CACHE_NUM_WORDS_PER_LINE/4) `define DDR2_CACHE_DDR2_SIDE_ADDR_WIDTH_NUM_WORDS_PER_LINE (`DDR2_CACHE_ADDR_WIDTH_WORDS_PER_LINE - 2) `define DDR2_CACHE_DDR2_SIDE_ADDR_WIDTH (`DDR2_CACHE_NUM_LINES_ENC_WIDTH + `DDR2_CACHE_DDR2_SIDE_ADDR_WIDTH_NUM_WORDS_PER_LINE) `define DDR2_CACHE_TAG_BITS 31:(`DDR2_CACHE_ADDR_WIDTH_WORDS_PER_LINE+2) wire ddr2_clk; // DDR2 iface domain clock. wire ddr2_rst; // reset from the ddr2 module wire wb_req; reg wb_req_r; reg wb_ack_o_r; wire wb_req_new; reg wb_req_new_r; wire wb_req_addr_hit; wire cached_addr_valid; wire [31:(32 -`DDR2_CACHE_TAG_ADDR_WIDTH)] cached_addr; // Spartan-6 MIG doesn't have any defines for the DDR2 burst length, // only burst length for user data. // Our user port is 128-bit //`define DDR2_BURSTLENGTH_1 //`define DDR2_BURSTLENGTH_2 //`define DDR2_BURSTLENGTH_4 //`define DDR2_BURSTLENGTH_8 `define DDR2_BURSTLENGTH_16 `ifdef DDR2_BURSTLENGTH_1 `define DDR2_BURST_DW128_ADDR_WIDTH 2 // = log2(burst of 1 128-bits = 4 words) `define DDR2_ADDR_ALIGN 4 `elsif DDR2_BURSTLENGTH_2 `define DDR2_BURST_DW128_ADDR_WIDTH 3 // = log2(burst of 2 128-bits = 8 words) `define DDR2_ADDR_ALIGN 5 `elsif DDR2_BURSTLENGTH_4 `define DDR2_BURST_DW128_ADDR_WIDTH 4 // = log2(burst of 4 128-bits = 16 words) `define DDR2_ADDR_ALIGN 6 `elsif DDR2_BURSTLENGTH_8 `define DDR2_BURST_DW128_ADDR_WIDTH 5 // = log2(burst of 8 128-bits = 32 words) `define DDR2_ADDR_ALIGN 7 `elsif DDR2_BURSTLENGTH_16 `define DDR2_BURST_DW128_ADDR_WIDTH 6 // = log2(burst of 16 128-bits = 64 words) `define DDR2_ADDR_ALIGN 8 `endif // This counts how many addresses we should write to the fifo - the number // of discrete FIFO transactions. reg [`DDR2_CACHE_ADDR_WIDTH_WORDS_PER_LINE-`DDR2_BURST_DW128_ADDR_WIDTH - 1:0] addr_counter; wire cache_write; wire cache_hit; wire wb_cache_en; reg do_writeback, do_writeback_r; wire do_writeback_start, do_writeback_finished; // Wire to indicate writing to data FIFO of MIG has completed reg do_writeback_data_finished; // Wire to indicate that address FIFO of MIG should be written to to // initiate memory accesses. reg do_writeback_addresses, do_writeback_addresses_r; reg do_readfrom, do_readfrom_r; wire do_readfrom_start, do_readfrom_finished; wire doing_readfrom; reg do_af_write; reg do_writeback_ddr2_fifo_we; reg ddr2_write_done; reg [`DDR2_CACHE_DDR2_SIDE_ADDR_WIDTH_NUM_WORDS_PER_LINE - 1:0] ddr2_cache_line_word_addr; wire [127:0] ddr2_cache_data_o; reg rd_data_valid_r; reg ddr2_read_done; // DDR2 MIG interface wires wire ddr2_p0_cmd_en; wire [30:0] ddr2_p0_cmd_byte_addr; wire [2:0] ddr2_p0_cmd_instr; wire ddr2_p0_cmd_full; wire ddr2_p0_cmd_empty; wire [5:0] ddr2_p0_cmd_bl; wire ddr2_p0_wr_en; wire [(C3_P0_DATA_PORT_SIZE)-1:0] ddr2_p0_wr_data; wire [(C3_P0_MASK_SIZE)-1:0] ddr2_p0_wr_mask; wire ddr2_p0_wr_full; wire ddr2_p0_wr_empty; wire [6:0] ddr2_p0_wr_count; wire ddr2_p0_wr_underrun; wire ddr2_p0_wr_error; wire ddr2_p0_rd_en; wire [(C3_P0_DATA_PORT_SIZE)-1:0] ddr2_p0_rd_data; wire ddr2_p0_rd_full; wire ddr2_p0_rd_empty; wire [6:0] ddr2_p0_rd_count; wire ddr2_p0_rd_overflow; wire ddr2_p0_rd_error; wire ddr2_calib_done; reg [1:0] ddr2_calib_done_r; wire [30:0] readfrom_af_addr; wire [30:0] writeback_af_addr; wire [`DDR2_CACHE_NUM_LINES - 1 :0] cache_line_addr_validate; wire [`DDR2_CACHE_NUM_LINES - 1 :0] cache_line_addr_invalidate; wire [`DDR2_CACHE_NUM_LINES - 1 :0] cache_line_addr_valid; wire [`DDR2_CACHE_NUM_LINES - 1 :0] cache_line_hit; wire [`DDR2_CACHE_TAG_BITS] cache_line_addr [0:`DDR2_CACHE_NUM_LINES-1] ; // Cache control signals // Wishbone side wire [`DDR2_CACHE_ADDR_WIDTH_WORDS_PER_LINE-1:0] wb_cache_adr; wire [3:0] wb_cache_sel_we; // DDR side wire ddr2_cache_en; wire [15:0] ddr2_cache_we; reg wb_bursting; // Indicate if burst is enabled reg [3:0] wb_burst_addr; // Burst counter, up to 16 wire [1:0] wb_burst_addr_4beat; wire [2:0] wb_burst_addr_8beat; wire wb_burst_addr_incr; wire ack_err; reg ack_err_r; // Synchronisation signals reg sync, sync_r; wire sync_start; wire sync_done; // Decoded select line wire [`DDR2_CACHE_NUM_LINES-1:0] selected_cache_line; wire [`DDR2_CACHE_NUM_LINES_ENC_WIDTH-1:0] selected_cache_line_enc; genvar i; generate for (i=0;i<`DDR2_CACHE_NUM_LINES;i=i+1) begin : cache_addr xilinx_ddr2_wb_if_cache_adr_reg cache_addr_reg_inst ( .adr_i(wb_adr_i[`DDR2_CACHE_TAG_BITS]), .validate(cache_line_addr_validate[i]), .invalidate(cache_line_addr_invalidate[i]), .cache_hit(cache_line_hit[i]), .adr_valid(cache_line_addr_valid[i]), .cached_adr_o(cache_line_addr[i]), .clk(wb_clk), .rst(wb_rst)); end endgenerate wire start_writeback, start_fill; xilinx_ddr2_wb_if_cache_control xilinx_ddr2_wb_if_cache_control0 ( // Outputs .start_writeback (start_writeback), .start_fill (start_fill), .cache_line_validate (cache_line_addr_validate), .cache_line_invalidate (cache_line_addr_invalidate), .selected_cache_line (selected_cache_line), .selected_cache_line_enc (selected_cache_line_enc), .sync_done (sync_done), // Inputs .cache_line_addr_valid (cache_line_addr_valid), .cache_line_addr_hit (cache_line_hit), .wb_req (wb_req), .cache_write (cache_write), .writeback_done (do_writeback_finished), .fill_done (do_readfrom_finished), .sync_start (sync_start), .wb_clk (wb_clk), .wb_rst (wb_rst)); defparam xilinx_ddr2_wb_if_cache_control0.num_lines = `DDR2_CACHE_NUM_LINES; defparam xilinx_ddr2_wb_if_cache_control0.num_lines_log2 = `DDR2_CACHE_NUM_LINES_ENC_WIDTH; assign cached_addr = selected_cache_line[0] ? cache_line_addr[0] : selected_cache_line[1] ? cache_line_addr[1] : selected_cache_line[2] ? cache_line_addr[2] : selected_cache_line[3] ? cache_line_addr[3] : 0; assign cache_write = wb_req & wb_we_i & wb_ack_o; assign cache_hit = |(selected_cache_line & cache_line_hit); assign cached_addr_valid = |(selected_cache_line & cache_line_addr_valid); assign wb_req_addr_hit = (wb_req & cache_hit & cached_addr_valid); // Wishbone request detection assign wb_req = wb_stb_i & wb_cyc_i & ddr2_calib_done_r[0] & !sync; always @ (posedge wb_clk) ddr2_calib_done_r[1:0] <= {ddr2_calib_done, ddr2_calib_done_r[1]}; always @(posedge wb_clk) wb_req_r <= wb_req; assign wb_req_new = wb_req & !wb_req_r; always @(posedge wb_clk) wb_req_new_r <= wb_req_new; always @(posedge wb_clk) if (wb_rst) wb_bursting <= 0; // Reset if acking end of transfer else if (wb_ack_o && wb_cti_i == 3'b111) wb_bursting <= 0; // Set if beginning new transaction and incrementing burst indicated // TODO - double check if this burst is going to go over a cache line // boundary - if so don't allow burst, fall back to classic cycles. else if (wb_req_new) wb_bursting <= (wb_cti_i == 3'b010); // Help constrain additions to appropriate bit-width for wrapping assign wb_burst_addr_4beat = wb_adr_i[3:2] + 1; assign wb_burst_addr_8beat = wb_adr_i[4:2] + 1; // Increment burst address whenever we get a hit when reading, or // when acking and writing. assign wb_burst_addr_incr = (wb_req_addr_hit & (!wb_we_i | (wb_we_i & wb_ack_o))); // Calculate burst address depending on burst type indicator always @(posedge wb_clk) if (wb_rst) wb_burst_addr <= 0; else if (wb_req_new) // When we have a bursting read to an address which is in cache then // initialise the address to the next word in the burst sequence. // If it's a miss, or it's a write, then we just take what's on the // bus. wb_burst_addr <= !(wb_req_addr_hit & !wb_we_i) ? wb_adr_i[5:2] : wb_bte_i==2'b01 ? {wb_adr_i[5:4], wb_burst_addr_4beat }: wb_bte_i==2'b10 ? {wb_adr_i[5], wb_burst_addr_8beat }: wb_bte_i==2'b11 ? wb_adr_i[5:2] + 1 : wb_adr_i[5:2]; else if (wb_burst_addr_incr & wb_bte_i==2'b01) wb_burst_addr[1:0] <= wb_burst_addr[1:0] + 1; else if (wb_burst_addr_incr & wb_bte_i==2'b10) wb_burst_addr[2:0] <= wb_burst_addr[2:0] + 1; else if (wb_burst_addr_incr & wb_bte_i==2'b11) wb_burst_addr[3:0] <= wb_burst_addr[3:0] + 1; `ifdef ERR_COUNTER reg [26:0] ack_err_cntr; always @(posedge wb_clk) if (wb_rst) ack_err_cntr <= 0; else if (!wb_req) ack_err_cntr <= 0; else if (|ack_err_cntr) ack_err_cntr <= ack_err_cntr + 1; else if (wb_req_new & !(|ack_err_cntr)) ack_err_cntr <= 1; assign ack_err = (&ack_err_cntr); always @(posedge wb_clk) ack_err_r <= ack_err; assign wb_err_o = ack_err_r; `else // !`ifdef ERR_COUNTER assign ack_err = 0; always @(posedge wb_clk) ack_err_r <= 0; assign wb_err_o = 0; `endif always @(posedge wb_clk) if (wb_rst) wb_ack_o <= 0; else wb_ack_o <= wb_req_addr_hit & ( // Simple acks on classic cycles (!wb_bursting && !wb_ack_o && !wb_ack_o_r) // De-assert ack when we see the final transaction || (wb_bursting && !(wb_cti_i==3'b111)) ); always @(posedge wb_clk) wb_ack_o_r <= wb_ack_o; // Logic controling synchronisation always @(posedge wb_clk) if (wb_rst) sync <= 0; else if (sync_done) // Sync. done indicator from cache controller sync <= 0; always @(posedge wb_clk) sync_r <= sync; assign sync_start = sync & !sync_r; task do_sync; begin // Wait for us to not be doing a transaction. while(wb_req) @wb_clk; // Cache not busy, initiate sync. sync = 1; end endtask // do_sync // Writeback/readfrom lower address generation always @(posedge wb_clk) if (wb_rst) addr_counter <= 0; else if (ddr2_p0_cmd_en) addr_counter <= addr_counter+1; // Determine if we're writing access requests into DDR2 interface AF always @(posedge wb_clk) if (wb_rst) do_af_write <= 0; else if (do_readfrom_start | do_writeback_data_finished) do_af_write <= 1; else if ((&addr_counter) & !ddr2_p0_cmd_full) // Stop when counter rolls over do_af_write <= 0; // Wishbone side of cache enable. Always enabled unless doing DDR2-side // things (fill or writeback). assign wb_cache_en = !(do_readfrom | do_writeback); // Writeback detect logic always @(posedge wb_clk) if (wb_rst) do_writeback <= 0; else if (start_writeback) do_writeback <= 1; else if (&ddr2_cache_line_word_addr) do_writeback <= 0; always @(posedge wb_clk) do_writeback_r <= do_writeback; // Detect falling edge of do_writeback always @(posedge wb_clk) do_writeback_data_finished <= !do_writeback & do_writeback_r; always @(posedge wb_clk) if (wb_rst) do_writeback_addresses <= 0; else if (do_writeback_data_finished) do_writeback_addresses <= 1; else if ((&addr_counter) & !ddr2_p0_cmd_full) do_writeback_addresses <= 0; always @(posedge wb_clk) do_writeback_addresses_r <= do_writeback_addresses; // Detect rising edge of do_writeback assign do_writeback_start = do_writeback & !do_writeback_r; // Detect falling edge of address writing control signal assign do_writeback_finished = !do_writeback_addresses & do_writeback_addresses_r; // DDR2 Read detect logic always @(posedge wb_clk) if (wb_rst) do_readfrom <= 0; else if (start_fill) do_readfrom <= 1; else if ((&ddr2_cache_line_word_addr)) do_readfrom <= 0; always @(posedge wb_clk) do_readfrom_r <= do_readfrom; // Detect line fill request rising edge assign do_readfrom_start = do_readfrom & !do_readfrom_r; // Detect line fill request falling edge assign do_readfrom_finished = !do_readfrom & do_readfrom_r; assign doing_readfrom = do_readfrom | do_readfrom_r; // Address fifo signals assign ddr2_p0_cmd_en = ((do_readfrom_r & ddr2_p0_wr_empty) | (do_writeback_addresses_r & !ddr2_p0_wr_empty)) & !ddr2_p0_cmd_full & do_af_write; assign ddr2_p0_cmd_instr[0] = doing_readfrom; // 1 - read, 0 - write assign ddr2_p0_cmd_instr[2:1] = 0; assign writeback_af_addr = {cached_addr, addr_counter, `DDR2_ADDR_ALIGN'd0}; assign readfrom_af_addr = {wb_adr_i[`DDR2_CACHE_TAG_BITS], addr_counter, `DDR2_ADDR_ALIGN'd0}; assign ddr2_p0_cmd_byte_addr = doing_readfrom ? readfrom_af_addr : writeback_af_addr; assign ddr2_p0_wr_en = do_writeback_ddr2_fifo_we; assign ddr2_p0_wr_data = ddr2_cache_data_o; assign ddr2_p0_wr_mask = 0; always @(posedge wb_clk) if (wb_rst) ddr2_cache_line_word_addr <= 0; else if (!ddr2_p0_rd_empty | (do_writeback & !ddr2_p0_wr_full)) ddr2_cache_line_word_addr <= ddr2_cache_line_word_addr + 1; else if (ddr2_write_done | ddr2_read_done) ddr2_cache_line_word_addr <= 0; always @(posedge wb_clk) do_writeback_ddr2_fifo_we <= (do_writeback & !ddr2_p0_wr_full); always @(posedge wb_clk) if (wb_rst) ddr2_write_done <= 0; else if ((&ddr2_cache_line_word_addr) & do_writeback) ddr2_write_done <= 1; else if (!do_writeback) // sample WB domain ddr2_write_done <= 0; always @(posedge wb_clk) rd_data_valid_r <= !ddr2_p0_rd_empty; // Read done signaling to WB domain always @(posedge wb_clk) if (wb_rst) ddr2_read_done <= 0; else if (rd_data_valid_r & (&ddr2_cache_line_word_addr)) ddr2_read_done <= 1; else if (!do_readfrom) // Read WB domain ddr2_read_done <= 0; // Lower word address uses potentially bursting address counter assign wb_cache_adr = wb_bursting ? {wb_adr_i[(`DDR2_CACHE_ADDR_WIDTH_WORDS_PER_LINE+2)-1:6],wb_burst_addr}: wb_adr_i[(`DDR2_CACHE_ADDR_WIDTH_WORDS_PER_LINE+2)-1:2]; assign wb_cache_sel_we = {4{wb_we_i & wb_ack_o}} & wb_sel_i; assign ddr2_cache_en = (!ddr2_p0_rd_empty |do_writeback); assign ddr2_cache_we = {16{!ddr2_p0_rd_empty}}; // Read enable always on assign ddr2_p0_rd_en = 1'b1; `ifdef DDR2_BURSTLENGTH_1 assign ddr2_p0_cmd_bl = 0; // burst of 1 * 128-bit `elsif DDR2_BURSTLENGTH_2 assign ddr2_p0_cmd_bl = 1; // burst of 2 * 128-bit `elsif DDR2_BURSTLENGTH_4 assign ddr2_p0_cmd_bl = 3; // burst of 4 * 128-bit `elsif DDR2_BURSTLENGTH_8 assign ddr2_p0_cmd_bl = 7; // burst of 8 * 128-bit `elsif DDR2_BURSTLENGTH_16 assign ddr2_p0_cmd_bl = 15; // burst of 16 * 128-bit `endif // Xilinx Coregen true dual-port RAMB // Wishbone side : 32-bit // DDR2 side : 128-bit xilinx_ddr2_if_cache cache_mem0 ( // Wishbone side .clka(wb_clk), .ena(wb_cache_en), .wea(wb_cache_sel_we), .addra({2'd0, selected_cache_line_enc,wb_cache_adr}), .dina(wb_dat_i), .douta(wb_dat_o), // DDR2 controller side .clkb(wb_clk), .enb(ddr2_cache_en), .web(ddr2_cache_we), .addrb({2'd0, selected_cache_line_enc, ddr2_cache_line_word_addr}), .dinb(ddr2_p0_rd_data), .doutb(ddr2_cache_data_o)); ddr2_mig # ( .C3_P0_MASK_SIZE (C3_P0_MASK_SIZE), .C3_P0_DATA_PORT_SIZE (C3_P0_DATA_PORT_SIZE), .DEBUG_EN (DEBUG_EN), .C3_MEMCLK_PERIOD (C3_MEMCLK_PERIOD), .C3_CALIB_SOFT_IP (C3_CALIB_SOFT_IP), .C3_SIMULATION (C3_SIMULATION), .C3_RST_ACT_LOW (C3_RST_ACT_LOW), .C3_INPUT_CLK_TYPE (C3_INPUT_CLK_TYPE), .C3_MEM_ADDR_ORDER (C3_MEM_ADDR_ORDER), .C3_NUM_DQ_PINS (C3_NUM_DQ_PINS), .C3_MEM_ADDR_WIDTH (C3_MEM_ADDR_WIDTH), .C3_MEM_BANKADDR_WIDTH (C3_MEM_BANKADDR_WIDTH) ) ddr2_mig ( .mcb3_dram_dq (ddr2_dq), .mcb3_dram_a (ddr2_a), .mcb3_dram_ba (ddr2_ba), .mcb3_dram_ras_n (ddr2_ras_n), .mcb3_dram_cas_n (ddr2_cas_n), .mcb3_dram_we_n (ddr2_we_n), .mcb3_dram_odt (ddr2_odt), .mcb3_dram_cke (ddr2_cke), .mcb3_dram_dm (ddr2_dm), .mcb3_dram_udqs (ddr2_udqs), .mcb3_dram_udqs_n (ddr2_udqs_n), .mcb3_rzq (ddr2_rzq), .mcb3_zio (ddr2_zio), .mcb3_dram_udm (ddr2_udm), .c3_sys_clk (ddr2_if_clk), .c3_sys_rst_n (ddr2_if_rst), .c3_calib_done (ddr2_calib_done), .c3_clk0 (ddr2_clk), .c3_rst0 (ddr2_rst), .mcb3_dram_dqs (ddr2_dqs), .mcb3_dram_dqs_n (ddr2_dqs_n), .mcb3_dram_ck (ddr2_ck), .mcb3_dram_ck_n (ddr2_ck_n), .c3_p0_cmd_clk (wb_clk), .c3_p0_cmd_en (ddr2_p0_cmd_en), .c3_p0_cmd_instr (ddr2_p0_cmd_instr), .c3_p0_cmd_bl (ddr2_p0_cmd_bl), .c3_p0_cmd_byte_addr (ddr2_p0_cmd_byte_addr[29:0]), .c3_p0_cmd_empty (ddr2_p0_cmd_empty), .c3_p0_cmd_full (ddr2_p0_cmd_full), .c3_p0_wr_clk (wb_clk), .c3_p0_wr_en (ddr2_p0_wr_en), .c3_p0_wr_mask (ddr2_p0_wr_mask), .c3_p0_wr_data (ddr2_p0_wr_data), .c3_p0_wr_full (ddr2_p0_wr_full), .c3_p0_wr_empty (ddr2_p0_wr_empty), .c3_p0_wr_count (ddr2_p0_wr_count), .c3_p0_wr_underrun (ddr2_p0_wr_underrun), .c3_p0_wr_error (ddr2_p0_wr_error), .c3_p0_rd_clk (wb_clk), .c3_p0_rd_en (ddr2_p0_rd_en), .c3_p0_rd_data (ddr2_p0_rd_data), .c3_p0_rd_full (ddr2_p0_rd_full), .c3_p0_rd_empty (ddr2_p0_rd_empty), .c3_p0_rd_count (ddr2_p0_rd_count), .c3_p0_rd_overflow (ddr2_p0_rd_overflow), .c3_p0_rd_error (ddr2_p0_rd_error) ); endmodule // xilinx_ddr2_if2 // Local Variables: // verilog-library-directories:("." "ddr2_mig") // verilog-library-extensions:(".v" ".h") // End: module xilinx_ddr2_wb_if_cache_adr_reg (adr_i, validate, invalidate, cached_adr_o, cache_hit, adr_valid, clk, rst); parameter full_adr_width = 32; parameter word_adr_width = 2; // 4 bytes per word parameter line_adr_width = 8; // 256 words per "line" parameter tag_width = full_adr_width - line_adr_width - word_adr_width; input [full_adr_width-1: word_adr_width + line_adr_width] adr_i; input validate; input invalidate; output [full_adr_width-1: word_adr_width + line_adr_width] cached_adr_o; output cache_hit; output reg adr_valid; input clk, rst; reg [tag_width-1:0] cached_adr; assign cached_adr_o = cached_adr; always @(posedge clk) if (rst) cached_adr <= 0; else if (validate) cached_adr <= adr_i; always @(posedge clk) if (rst) adr_valid <= 0; else if (validate) adr_valid <= 1; else if (invalidate) adr_valid <= 0; assign cache_hit = (adr_i == cached_adr); endmodule // xilinx_ddr2_wb_if_cache_adr_reg module xilinx_ddr2_wb_if_cache_control ( cache_line_addr_valid, cache_line_addr_hit, wb_req, cache_write, writeback_done, fill_done, sync_start, sync_done, start_writeback, start_fill, cache_line_validate, cache_line_invalidate, selected_cache_line, selected_cache_line_enc, wb_clk, wb_rst); parameter num_lines = 16; parameter num_lines_log2 = 4; input [num_lines-1:0] cache_line_addr_valid; input [num_lines-1:0] cache_line_addr_hit; input wb_req; input cache_write; input writeback_done, fill_done; input sync_start; output sync_done; output reg start_writeback; output reg start_fill; output reg [num_lines-1:0] cache_line_validate; output reg [num_lines-1:0] cache_line_invalidate; output [num_lines-1:0] selected_cache_line; output reg [num_lines_log2-1:0] selected_cache_line_enc; input wb_clk, wb_rst; reg [num_lines-1:0] lines_dirty; reg [num_lines-1:0] selected_cache_line_from_miss; reg selected_cache_line_new; reg invalidate_clean_line; reg [num_lines-1:0] selected_cache_line_r; reg [num_lines-1:0] selected_cache_line_r2; reg wb_req_r; wire wb_req_new; reg wb_req_new_r; parameter sync_line_check_wait = 4; reg [num_lines-1:0] sync_line_counter; reg sync_doing; reg [sync_line_check_wait-1:0] sync_line_select_wait_counter_shr; reg sync_line_done; wire sync_writeback_line; always @(posedge wb_clk) wb_req_r <= wb_req; assign wb_req_new = wb_req & !wb_req_r; always @(posedge wb_clk) wb_req_new_r <= wb_req_new; // Select a cache line when we miss. Currently very simply is round robin always @(posedge wb_clk) if (wb_rst) selected_cache_line_from_miss <= 1; else if (wb_req_new_r & !(|selected_cache_line_r)) // miss,no line selected // Shift select bit one selected_cache_line_from_miss <= {selected_cache_line_from_miss[num_lines-2:0], selected_cache_line_from_miss[num_lines-1]}; // Line selection logic, when line address is valid and hit, we select always @(posedge wb_clk) if (wb_rst) selected_cache_line_r <= 0; else if (wb_req_new) selected_cache_line_r <= cache_line_addr_valid & cache_line_addr_hit; else if (wb_req_new_r & !(|selected_cache_line_r)) selected_cache_line_r <= selected_cache_line_from_miss; else if (sync_doing) selected_cache_line_r <= sync_line_counter; always @(posedge wb_clk) selected_cache_line_r2 <= selected_cache_line_r; assign selected_cache_line = selected_cache_line_r2; // A new line of cache has been selected always @(posedge wb_clk) if (wb_rst) selected_cache_line_new <= 0; else if (wb_req_new & (&(cache_line_addr_valid & cache_line_addr_hit))) // New line address selected selected_cache_line_new <= 1; else if ((!selected_cache_line_new) & wb_req_new_r) // Didn't select one last time, so we must have forced ourselves to // select a new one selected_cache_line_new <= 1; else if (selected_cache_line_new) selected_cache_line_new <= 0; always @(posedge wb_clk) if (wb_rst) lines_dirty <= 0; else if (cache_write) lines_dirty <= lines_dirty | selected_cache_line_r; else if (writeback_done) lines_dirty <= lines_dirty & ~(selected_cache_line_r); // Validate the cache line address in the register when line filled always @(posedge wb_clk) if (wb_rst) cache_line_validate <= 0; else if (fill_done) cache_line_validate <= selected_cache_line_r; else if (|cache_line_validate) cache_line_validate <= 0; // Invalidate the cache line address in the register when line written back always @(posedge wb_clk) if (wb_rst) cache_line_invalidate <= 0; else if ((writeback_done & !sync_doing) | invalidate_clean_line) cache_line_invalidate <= selected_cache_line_r; else if (|cache_line_invalidate) cache_line_invalidate <= 0; // Initiate-writeback logic always @(posedge wb_clk) if (wb_rst) start_writeback <= 0; else if (start_writeback) start_writeback <= 0; else if (selected_cache_line_new & (|(lines_dirty & selected_cache_line_r)) & (|(selected_cache_line_r & cache_line_addr_valid)) & !(|(cache_line_addr_hit & selected_cache_line_r))) start_writeback <= 1; else if (sync_writeback_line) start_writeback <= 1; // Invalidate lines which we haven't written to so we can fill them always @(posedge wb_clk) if (wb_rst) invalidate_clean_line <= 0; else if (invalidate_clean_line) invalidate_clean_line <= 0; else if ((selected_cache_line_new) & // New line selected !(|(lines_dirty & selected_cache_line_r)) & // It's not dirty // It's valid, but we've selected it so we're trashing it (|(selected_cache_line_r & cache_line_addr_valid)) & !(|(cache_line_addr_hit & selected_cache_line_r))) // Not a hit invalidate_clean_line <= 1; reg invalidate_clean_line_r; always @(posedge wb_clk) invalidate_clean_line_r <= invalidate_clean_line; // Initiate-fill logic always @(posedge wb_clk) if (wb_rst) start_fill <= 0; else if (((selected_cache_line_new) & // New line selected // not valid !(|(cache_line_addr_valid & selected_cache_line_r))) | (writeback_done & !sync_doing) | invalidate_clean_line_r ) start_fill <= 1; else if (start_fill) start_fill <= 0; // Hardcoded to 4 lines currently. always @(posedge wb_clk) if (selected_cache_line_r[0]) selected_cache_line_enc <= 0; else if (selected_cache_line_r[1]) selected_cache_line_enc <= 1; else if (selected_cache_line_r[2]) selected_cache_line_enc <= 2; else if (selected_cache_line_r[3]) selected_cache_line_enc <= 3; // Synchronisation control always @(posedge wb_clk) if (wb_rst) sync_doing <= 0; else if (sync_start) sync_doing <= 1; else if (sync_done) sync_doing <= 0; always @(posedge wb_clk) if (wb_rst) sync_line_counter <= 0; else if (sync_start) // Set first line to check sync_line_counter[0] <= 1'b1; else if (sync_line_done) // Shift along, check next line sync_line_counter <= {sync_line_counter[num_lines-2:0], 1'b0}; // Pulse this on finishing of checking lines assign sync_done = sync_line_counter[num_lines-1] & sync_line_done; // Pulses when a dirty line is detected and should be written back. assign sync_writeback_line = sync_doing & sync_line_select_wait_counter_shr[0] & cache_line_addr_valid & |(sync_line_counter & lines_dirty); always @(posedge wb_clk) if (wb_rst) sync_line_select_wait_counter_shr <= 0; else if (|sync_line_select_wait_counter_shr) sync_line_select_wait_counter_shr <= {1'b0,sync_line_select_wait_counter_shr[sync_line_check_wait-1:1]}; else if (sync_start | (sync_line_done & !sync_done)) sync_line_select_wait_counter_shr[sync_line_check_wait-1] <= 1'b1; always @(posedge wb_clk) if (wb_rst) sync_line_done <= 1'b0; else if (sync_line_done) sync_line_done <= 1'b0; // Either line doesn't need writeback else if (sync_line_select_wait_counter_shr[0] & !sync_writeback_line) sync_line_done <= 1'b1; // Or writeback finished else if (writeback_done & sync_doing) sync_line_done <= 1'b1; endmodule // xilinx_ddr2_wb_if_cache_control