diff --git a/wally-pipelined/src/cache/dmapped.sv b/wally-pipelined/src/cache/dmapped.sv index f97cfa2d..fb6ce4c5 100644 --- a/wally-pipelined/src/cache/dmapped.sv +++ b/wally-pipelined/src/cache/dmapped.sv @@ -4,8 +4,7 @@ // Written: jaallen@g.hmc.edu 2021-03-23 // Modified: // -// Purpose: An implementation of a direct-mapped cache memory -// This cache is read-only, so "write"s to the memory are loading new data +// Purpose: An implementation of a direct-mapped cache memory, with read-only and write-through versions // // A component of the Wally configurable RISC-V project. // @@ -26,6 +25,7 @@ `include "wally-config.vh" +// Read-only direct-mapped memory module rodirectmappedmem #(parameter NUMLINES=512, parameter LINESIZE = 256, parameter WORDSIZE = `XLEN) ( // Pipeline stuff input logic clk, @@ -124,3 +124,110 @@ module rodirectmappedmem #(parameter NUMLINES=512, parameter LINESIZE = 256, par end assign DataValid = DataValidBit && (DataTag == ReadTag); endmodule + +// Write-through direct-mapped memory +module wtdirectmappedmem #(parameter NUMLINES=512, parameter LINESIZE = 256, parameter WORDSIZE = `XLEN) ( + // Pipeline stuff + input logic clk, + input logic reset, + input logic stall, + // If flush is high, invalidate the entire cache + input logic flush, + // Select which address to read (broken for efficiency's sake) + input logic [`XLEN-1:12] ReadUpperPAdr, + input logic [11:0] ReadLowerAdr, + // Load new data into the cache (from main memory) + input logic LoadEnable, + input logic [LINESIZE-1:0] LoadLine, + input logic [`XLEN-1:0] LoadPAdr, + // Write data to the cache (like from a store instruction) + input logic WriteEnable, + input logic [WORDSIZE-1:0] WriteWord, + input logic [`XLEN-1:0] WritePAdr, + input logic [1:0] WriteSize, // Specify size of the write (non-written bits should be preserved) + // Output the word, as well as if it is valid + output logic [WORDSIZE-1:0] DataWord, + output logic DataValid +); + + // Various compile-time constants + localparam integer WORDWIDTH = $clog2(WORDSIZE/8); + localparam integer OFFSETWIDTH = $clog2(LINESIZE/WORDSIZE); + localparam integer SETWIDTH = $clog2(NUMLINES); + localparam integer TAGWIDTH = `XLEN - OFFSETWIDTH - SETWIDTH - WORDWIDTH; + + localparam integer OFFSETBEGIN = WORDWIDTH; + localparam integer OFFSETEND = OFFSETBEGIN+OFFSETWIDTH-1; + localparam integer SETBEGIN = OFFSETEND+1; + localparam integer SETEND = SETBEGIN + SETWIDTH - 1; + localparam integer TAGBEGIN = SETEND + 1; + localparam integer TAGEND = TAGBEGIN + TAGWIDTH - 1; + + // Machinery to read from and write to the correct addresses in memory + logic [`XLEN-1:0] ReadPAdr; + logic [`XLEN-1:0] OldReadPAdr; + logic [OFFSETWIDTH-1:0] ReadOffset, LoadOffset; + logic [SETWIDTH-1:0] ReadSet, LoadSet; + logic [TAGWIDTH-1:0] ReadTag, LoadTag; + logic [LINESIZE-1:0] ReadLine; + logic [LINESIZE/WORDSIZE-1:0][WORDSIZE-1:0] ReadLineTransformed; + + // Machinery to check if a given read is valid and is the desired value + logic [TAGWIDTH-1:0] DataTag; + logic [NUMLINES-1:0] ValidOut; + logic DataValidBit; + + flopenr #(`XLEN) ReadPAdrFlop(clk, reset, ~stall, ReadPAdr, OldReadPAdr); + + // Assign the read and write addresses in cache memory + always_comb begin + ReadOffset = OldReadPAdr[OFFSETEND:OFFSETBEGIN]; + ReadPAdr = {ReadUpperPAdr, ReadLowerAdr}; + ReadSet = ReadPAdr[SETEND:SETBEGIN]; + ReadTag = OldReadPAdr[TAGEND:TAGBEGIN]; + + LoadOffset = LoadPAdr[OFFSETEND:OFFSETBEGIN]; + LoadSet = LoadPAdr[SETEND:SETBEGIN]; + LoadTag = LoadPAdr[TAGEND:TAGBEGIN]; + end + + // Depth is number of bits in one "word" of the memory, width is number of such words + Sram1Read1Write #(.DEPTH(LINESIZE), .WIDTH(NUMLINES)) cachemem ( + .*, + .ReadAddr(ReadSet), + .ReadData(ReadLine), + .WriteAddr(LoadSet), + .WriteData(LoadLine), + .WriteEnable(LoadEnable) + ); + Sram1Read1Write #(.DEPTH(TAGWIDTH), .WIDTH(NUMLINES)) cachetags ( + .*, + .ReadAddr(ReadSet), + .ReadData(DataTag), + .WriteAddr(LoadSet), + .WriteData(LoadTag), + .WriteEnable(LoadEnable) + ); + + // Pick the right bits coming out the read line + assign DataWord = ReadLineTransformed[ReadOffset]; + genvar i; + generate + for (i=0; i < LINESIZE/WORDSIZE; i++) begin + assign ReadLineTransformed[i] = ReadLine[(i+1)*WORDSIZE-1:i*WORDSIZE]; + end + endgenerate + + // Correctly handle the valid bits + always_ff @(posedge clk, posedge reset) begin + if (reset || flush) begin + ValidOut <= {NUMLINES{1'b0}}; + end else begin + if (LoadEnable) begin + ValidOut[LoadSet] <= 1; + end + end + DataValidBit <= ValidOut[ReadSet]; + end + assign DataValid = DataValidBit && (DataTag == ReadTag); +endmodule diff --git a/wally-pipelined/src/dmem/dcache.sv b/wally-pipelined/src/dmem/dcache.sv new file mode 100644 index 00000000..243c6975 --- /dev/null +++ b/wally-pipelined/src/dmem/dcache.sv @@ -0,0 +1,184 @@ +/////////////////////////////////////////// +// dcache.sv +// +// Written: jaallen@g.hmc.edu 2021-04-15 +// Modified: +// +// Purpose: Cache memory for the dmem so it can access memory less often, saving cycles +// +// A component of the Wally configurable RISC-V project. +// +// Copyright (C) 2021 Harvey Mudd College & Oklahoma State University +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +// OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////// + +`include "wally-config.vh" + +module dcache( + // Basic pipeline stuff + input logic clk, reset, + input logic StallW, + input logic FlushW, + // Upper bits of physical address + input logic [`XLEN-1:12] UpperPAdrM, + // Lower 12 bits of virtual address, since it's faster this way + input logic [11:0] LowerVAdrM, + // Write to the dcache + input logic [`XLEN-1:0] DCacheWriteDataM, + input logic DCacheReadM, DCacheWriteM, + // Data read in from the ebu unit + input logic [`XLEN-1:0] ReadDataW, + input logic MemAckW, + // Access requested from the ebu unit + output logic [`XLEN-1:0] MemPAdrM, + output logic MemReadM, MemWriteM, + // High if the dcache is requesting a stall + output logic DCacheStallW, + // The data that was requested from the cache + output logic [`XLEN-1:0] DCacheReadW +); + + // Configuration parameters + // TODO Move these to a config file + localparam integer DCACHELINESIZE = 256; + localparam integer DCACHENUMLINES = 512; + + // Input signals to cache memory + logic FlushMem; + logic [`XLEN-1:12] DCacheMemUpperPAdr; + logic [11:0] DCacheMemLowerAdr; + logic DCacheMemWriteEnable; + logic [DCACHELINESIZE-1:0] DCacheMemWriteData; + logic [`XLEN-1:0] DCacheMemWritePAdr; + logic EndFetchState; + // Output signals from cache memory + logic [`XLEN-1:0] DCacheMemReadData; + logic DCacheMemReadValid; + + wtdirectmappedmem #(.LINESIZE(DCACHELINESIZE), .NUMLINES(DCACHENUMLINES), .WORDSIZE(`XLEN)) cachemem( + .*, + // Stall it if the pipeline is stalled, unless we're stalling it and we're ending our stall + .stall(StallW), + .flush(FlushMem), + .ReadUpperPAdr(DCacheMemUpperPAdr), + .ReadLowerAdr(DCacheMemLowerAdr), + .LoadEnable(DCacheMemWriteEnable), + .LoadLine(DCacheMemWriteData), + .LoadPAdr(DCacheMemWritePAdr), + .DataWord(DCacheMemReadData), + .DataValid(DCacheMemReadValid), + .WriteEnable(0), + .WriteWord(0), + .WritePAdr(0), + .WriteSize(2'b10) + ); + + dcachecontroller #(.LINESIZE(DCACHELINESIZE)) controller(.*); + + // For now, assume no writes to executable memory + assign FlushMem = 1'b0; +endmodule + +module dcachecontroller #(parameter LINESIZE = 256) ( + // Inputs from pipeline + input logic clk, reset, + input logic StallW, + input logic FlushW, + + // Input the address to read + // The upper bits of the physical pc + input logic [`XLEN-1:12] DCacheMemUpperPAdr, + // The lower bits of the virtual pc + input logic [11:0] DCacheMemLowerAdr, + + // Signals to/from cache memory + // The read coming out of it + input logic [`XLEN-1:0] DCacheMemReadData, + input logic DCacheMemReadValid, + // Load data into the cache + output logic DCacheMemWriteEnable, + output logic [LINESIZE-1:0] DCacheMemWriteData, + output logic [`XLEN-1:0] DCacheMemWritePAdr, + + // The read that was requested + output logic [31:0] DCacheReadW, + + // Outputs to pipeline control stuff + output logic DCacheStallW, EndFetchState, + + // Signals to/from ahblite interface + // A read containing the requested data + input logic [`XLEN-1:0] ReadDataW, + input logic MemAckW, + // The read we request from main memory + output logic [`XLEN-1:0] MemPAdrM, + output logic MemReadM, MemWriteM +); + + // Cache fault signals + logic FaultStall; + + // Handle happy path (data in cache) + + always_comb begin + DCacheReadW = DCacheMemReadData; + end + + + // Handle cache faults + + localparam integer WORDSPERLINE = LINESIZE/`XLEN; + localparam integer LOGWPL = $clog2(WORDSPERLINE); + localparam integer OFFSETWIDTH = $clog2(LINESIZE/8); + + logic FetchState, BeginFetchState; + logic [LOGWPL:0] FetchWordNum, NextFetchWordNum; + logic [`XLEN-1:0] LineAlignedPCPF; + + flopr #(1) FetchStateFlop(clk, reset, BeginFetchState | (FetchState & ~EndFetchState), FetchState); + flopr #(LOGWPL+1) FetchWordNumFlop(clk, reset, NextFetchWordNum, FetchWordNum); + + genvar i; + generate + for (i=0; i < WORDSPERLINE; i++) begin + flopenr #(`XLEN) flop(clk, reset, FetchState & (i == FetchWordNum), ReadDataW, DCacheMemWriteData[(i+1)*`XLEN-1:i*`XLEN]); + end + endgenerate + + // Enter the fetch state when we hit a cache fault + always_comb begin + BeginFetchState = ~DCacheMemReadValid & ~FetchState & (FetchWordNum == 0); + end + // Exit the fetch state once the cache line has been loaded + flopr #(1) EndFetchStateFlop(clk, reset, DCacheMemWriteEnable, EndFetchState); + + // Machinery to request the correct addresses from main memory + always_comb begin + MemReadM = FetchState & ~EndFetchState & ~DCacheMemWriteEnable; + LineAlignedPCPF = {DCacheMemUpperPAdr, DCacheMemLowerAdr[11:OFFSETWIDTH], {OFFSETWIDTH{1'b0}}}; + MemPAdrM = LineAlignedPCPF + FetchWordNum*(`XLEN/8); + NextFetchWordNum = FetchState ? FetchWordNum+MemAckW : {LOGWPL+1{1'b0}}; + end + + // Write to cache memory when we have the line here + always_comb begin + DCacheMemWritePAdr = LineAlignedPCPF; + DCacheMemWriteEnable = FetchWordNum == {1'b1, {LOGWPL{1'b0}}} & FetchState & ~EndFetchState; + end + + // Stall the pipeline while loading a new line from memory + always_comb begin + DCacheStallW = FetchState | ~DCacheMemReadValid; + end +endmodule