123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- /*
- * L2 Cache
- * Sits between CPU and SDRAM controller
- * Made to run at 100MHz
- */
- module L2cache(
- // clock/reset inputs
- input clk,
- input reset,
- // CPU bus
- input [23:0] l2_addr,
- input [31:0] l2_data,
- input l2_we,
- input l2_start,
- output [31:0] l2_q,
- output l2_done,
- // SDRAM controller bus
- output [23:0] sdc_addr,
- output [31:0] sdc_data,
- output sdc_we,
- output sdc_start,
- input [31:0] sdc_q,
- input sdc_done
- );
- wire cache_reset;
- assign cache_reset = 1'b0;
- parameter cache_size = 1024; // cache size in words. 8129*4bytes = 32KiB
- parameter index_size = 10; // index size: log2(cache_size)
- parameter tag_size = 14; // mem_add_bits-index_size = 24-13 = 11
- parameter cache_line_size = tag_size+32; // tag + word
- reg [cache_line_size-1:0] cache [0:cache_size-1]; // cache memory
- integer i;
- // init cache to all zeros
- initial
- begin
- for (i = 0; i < cache_size; i = i + 1)
- begin
- cache[i] = 46'd0;
- end
- end
- reg [index_size-1:0] cache_addr = 10'd0;
- reg [cache_line_size-1:0] cache_d = 46'd0;
- reg cache_we = 1'b0;
- reg [cache_line_size-1:0] cache_q = 46'd0;
- always @(posedge clk)
- begin
- cache_q <= cache[cache_addr];
- if (cache_we)
- begin
- cache[cache_addr] <= cache_d;
- $display("%d: wrote to l2 cache", $time);
- end
- end
- reg [31:0] l2_q_reg = 32'd0;
- reg l2_done_reg = 1'b0;
- reg [23:0] sdc_addr_reg = 24'd0;
- reg [31:0] sdc_data_reg = 32'd0;
- reg sdc_we_reg = 1'b0;
- reg sdc_start_reg = 1'b0;
- reg start_prev = 1'b0;
- // state machine
- reg [2:0] state = 3'd0; // 0-7 states limit
- parameter state_init = 3'd0;
- parameter state_idle = 3'd1;
- parameter state_writing = 3'd2;
- parameter state_check_cache = 3'd3;
- parameter state_miss_read_ram = 3'd4;
- parameter state_delay_cache = 3'd5;
- parameter state_done_high = 3'd6;
- //wire cache_hit = valid_bits[cache_addr] && l2_addr[23:index_size] == cache_q[42:32];
- // uninferrable valid bit memory
- reg [cache_size-1:0] valid_bits = 1024'd0;
- reg [9:0] valid_a = 10'd0;
- reg valid_d = 1'b0;
- reg valid_q = 1'b0;
- reg valid_we = 1'b0;
- always @(posedge clk)
- begin
- if (reset | cache_reset)
- begin
- valid_bits <= 1024'd0;
- end
- else
- begin
- valid_q <= valid_bits[valid_a];
- if (valid_we)
- begin
- valid_bits[valid_a] <= valid_d;
- $display("%d: wrote valid bit l2", $time);
- end
- end
- end
- reg [31:0] addr_prev = 32'd0;
- always @(posedge clk)
- begin
- if (reset)
- begin
- valid_a <= 10'd0;
- valid_d <= 1'b0;
- valid_we <= 1'b0;
- l2_q_reg <= 32'd0;
- l2_done_reg <= 1'b0;
- sdc_addr_reg <= 24'd0;
- sdc_data_reg <= 32'd0;
- sdc_we_reg <= 1'b0;
- sdc_start_reg <= 1'b0;
- addr_prev <= 32'd0;
-
- // Make sure the next cycle a new request can be detected!
- start_prev <= 1'b0;
- state <= state_idle;
- end
- else
- begin
- addr_prev <= l2_addr;
- start_prev <= l2_start;
- l2_done_reg <= 1'b0;
- cache_we <= 1'b0;
- valid_d <= 1'b0;
- valid_we <= 1'b0;
-
- // NOTE: make sure to use latched l2_addr from rising start to make sure all addresses are correct!
- // as l2_addr can change during a clear/skipresult (e.g. when jump or other pipeline flush)
- case(state)
- state_init:
- begin
- state <= state_idle;
- end
- state_idle:
- begin
- valid_a <= l2_addr[index_size-1:0];
- if (l2_addr < 27'h800000)
- begin
- if ( (l2_start && !start_prev) || addr_prev >= 27'h800000 && l2_start)
- begin
- if (l2_we)
- begin
- // update cache and write SDRAM
- state <= state_writing;
- sdc_addr_reg <= l2_addr;
- sdc_we_reg <= 1'b1;
- sdc_start_reg <= 1'b1;
- sdc_data_reg <= l2_data;
- cache_d <= {l2_addr[23:index_size], l2_data}; // tag + data
- cache_addr <= l2_addr[index_size-1:0];
-
- end
- else
- begin
- // wait a cycle for cache to be read
- cache_addr <= l2_addr[index_size-1:0];
- state <= state_delay_cache;
- // just in case we have a cache miss in the next cycle, prepare address on sdram controller bus
- sdc_addr_reg <= l2_addr;
- sdc_we_reg <= 1'b0;
- end
- end
- end
- end
- state_delay_cache:
- begin
- state <= state_check_cache;
- end
- state_writing:
- begin
- if (sdc_done)
- begin
- state <= state_done_high;
- sdc_addr_reg <= 24'd0;
- sdc_we_reg <= 1'b0;
- sdc_start_reg <= 1'b0;
- sdc_data_reg <= 32'd0;
- cache_we <= 1'b1;
- valid_d <= 1'b1;
- valid_we <= 1'b1;
- l2_done_reg <= 1'b1;
- end
- end
- state_check_cache:
- begin
- // check cache. if hit, return cached item
- if (valid_q && sdc_addr_reg[23:index_size] == cache_q[45:32]) // valid and tag check
- begin
- state <= state_done_high;
- l2_done_reg <= 1'b1;
- l2_q_reg <= cache_q[31:0];
- end
- // if miss, read from ram, place in cache, return cached item
- else
- begin
- state <= state_miss_read_ram;
- sdc_start_reg <= 1'b1;
- end
- end
- state_miss_read_ram:
- begin
- if (sdc_done)
- begin
- state <= state_done_high;
- // we received item from ram, now write to cache and return
- sdc_addr_reg <= 24'd0;
- sdc_start_reg <= 1'b0;
- cache_we <= 1'b1;
- cache_d <= {sdc_addr_reg[23:index_size], sdc_q}; // tag + data
-
- valid_d <= 1'b1;
- valid_we <= 1'b1;
- l2_done_reg <= 1'b1;
- l2_q_reg <= sdc_q;
- end
- end
- state_done_high:
- begin
- // keep done high for one cycle as we run on double clock speed from CPU
- l2_done_reg <= 1'b1;
- state <= state_idle;
- end
- endcase
- end
- end
- // passthrough when above SDRAM memory range
- assign sdc_addr = (l2_addr < 27'h800000) ? sdc_addr_reg : l2_addr;
- assign sdc_data = (l2_addr < 27'h800000) ? sdc_data_reg : l2_data;
- assign sdc_we = (l2_addr < 27'h800000) ? sdc_we_reg : l2_we;
- assign sdc_start = (l2_addr < 27'h800000) ? sdc_start_reg : l2_start;
- assign l2_q = (l2_addr < 27'h800000) ? l2_q_reg : sdc_q;
- assign l2_done = (l2_addr < 27'h800000) ? l2_done_reg : sdc_done;
- endmodule
|