diff --git a/wally-pipelined/regression/wally-coremark.do b/wally-pipelined/regression/wally-coremark.do index d5612238..056b34cb 100644 --- a/wally-pipelined/regression/wally-coremark.do +++ b/wally-pipelined/regression/wally-coremark.do @@ -43,46 +43,54 @@ add wave /testbench/clk add wave /testbench/reset add wave -divider #add wave /testbench/dut/hart/ebu/IReadF -add wave /testbench/dut/hart/DataStall -add wave /testbench/dut/hart/InstrStall -add wave /testbench/dut/hart/StallF -add wave /testbench/dut/hart/StallD -add wave /testbench/dut/hart/FlushD -add wave /testbench/dut/hart/FlushE -add wave /testbench/dut/hart/FlushM -add wave /testbench/dut/hart/FlushW +#add wave /testbench/dut/hart/DataStall +#add wave /testbench/dut/hart/InstrStall +#add wave /testbench/dut/hart/StallF +#add wave /testbench/dut/hart/StallD +#add wave /testbench/dut/hart/FlushD +#add wave /testbench/dut/hart/FlushE +#add wave /testbench/dut/hart/FlushM +#add wave /testbench/dut/hart/FlushW add wave -divider add wave -hex /testbench/dut/hart/ifu/PCF add wave -hex /testbench/dut/hart/ifu/InstrF add wave /testbench/InstrFName -#add wave -hex /testbench/dut/hart/ifu/PCD +add wave -divider +add wave -hex /testbench/dut/hart/ifu/PCD add wave -hex /testbench/dut/hart/ifu/InstrD add wave /testbench/InstrDName add wave -divider -#add wave -hex /testbench/dut/hart/ifu/PCE -#add wave -hex /testbench/dut/hart/ifu/InstrE +add wave -hex /testbench/dut/hart/ifu/PCE +add wave -hex /testbench/dut/hart/ifu/InstrE add wave /testbench/InstrEName -add wave -hex /testbench/dut/hart/ieu/dp/SrcAE -add wave -hex /testbench/dut/hart/ieu/dp/SrcBE -add wave -hex /testbench/dut/hart/ieu/dp/ALUResultE -add wave /testbench/dut/hart/ieu/dp/PCSrcE add wave -divider -#add wave -hex /testbench/dut/hart/ifu/PCM -#add wave -hex /testbench/dut/hart/ifu/InstrM +add wave -hex /testbench/dut/hart/ifu/PCM +add wave -hex /testbench/dut/hart/ifu/InstrM add wave /testbench/InstrMName -add wave /testbench/dut/uncore/dtim/memwrite -add wave -hex /testbench/dut/uncore/HADDR -add wave -hex /testbench/dut/uncore/HWDATA add wave -divider add wave -hex /testbench/dut/hart/ifu/PCW +add wave -hex /testbench/dut/hart/ifu/InstrW add wave /testbench/InstrWName -add wave /testbench/dut/hart/ieu/dp/RegWriteW -add wave -hex /testbench/dut/hart/ieu/dp/ResultW -add wave -hex /testbench/dut/hart/ieu/dp/RdW +#add wave -hex /testbench/dut/hart/ieu/dp/SrcAE +#add wave -hex /testbench/dut/hart/ieu/dp/SrcBE +#add wave -hex /testbench/dut/hart/ieu/dp/ALUResultE +#add wave /testbench/dut/hart/ieu/dp/PCSrcE add wave -divider -#add ww -add wave -hex -r /testbench/* +#add wave /testbench/dut/uncore/dtim/memwrite +#add wave -hex /testbench/dut/uncore/HADDR +#add wave -hex /testbench/dut/uncore/HWDATA +#add wave -divider +#add wave -hex /testbench/dut/hart/ifu/PCW +#add wave /testbench/InstrWName +#add wave /testbench/dut/hart/ieu/dp/RegWriteW +#add wave -hex /testbench/dut/hart/ieu/dp/ResultW +#add wave -hex /testbench/dut/hart/ieu/dp/RdW +#add wave -hex -r /testbench/* +add wave -hex -r /testbench/dut/hart/ieu/dp/regf/* +add wave -divider +add wave -divider +add wave -hex -r /testbench/dut/hart/ebu/ReadDataW -- Set Wave Output Items TreeUpdate [SetDefaultTree] @@ -98,6 +106,6 @@ configure wave -childrowmargin 2 set DefaultRadix hexadecimal -- Run the Simulation -#run 1000 -run -all +run 3000 +#run -all #quit diff --git a/wally-pipelined/src/dmem/dmem.sv b/wally-pipelined/src/dmem/dmem.sv index 1442b93c..aa4327c2 100644 --- a/wally-pipelined/src/dmem/dmem.sv +++ b/wally-pipelined/src/dmem/dmem.sv @@ -29,7 +29,7 @@ module dmem ( input logic clk, reset, - input logic FlushW, + input logic StallW, FlushW, //output logic DataStall, // Memory Stage input logic [1:0] MemRWM, @@ -37,34 +37,53 @@ module dmem ( input logic [2:0] Funct3M, //input logic [`XLEN-1:0] ReadDataW, input logic [`XLEN-1:0] WriteDataM, + input logic AtomicM, output logic [`XLEN-1:0] MemPAdrM, output logic MemReadM, MemWriteM, output logic DataMisalignedM, // Writeback Stage input logic MemAckW, input logic [`XLEN-1:0] ReadDataW, + output logic SquashSCW, // faults input logic DataAccessFaultM, output logic LoadMisalignedFaultM, LoadAccessFaultM, - output logic StoreMisalignedFaultM, StoreAccessFaultM + output logic StoreMisalignedFaultM, StoreAccessFaultM, + // TLB management + //input logic [`XLEN-1:0] PageTableEntryM, + //input logic DTLBWriteM, DTLBFlushM, + // *** satp value will come from CSRs + // input logic [`XLEN-1:0] SATP, + output logic DTLBMissM, DTLBHitM ); + logic SquashSCM; + // Initially no MMU - assign MemPAdrM = MemAdrM; + // *** temporary hack until we can figure out how to get actual satp value + // from priv unit -- Thomas F + logic [`XLEN-1:0] SATP = '0; + // *** temporary hack until walker is hooked up -- Thomas F + logic [`XLEN-1:0] PageTableEntryM = '0; + logic DTLBFlushM = '0; + logic DTLBWriteM = '0; + tlb #(3) dtlb(clk, reset, SATP, MemAdrM, PageTableEntryM, DTLBWriteM, + DTLBFlushM, MemPAdrM, DTLBMissM, DTLBHitM); + //assign MemPAdrM = MemAdrM; // Determine if an Unaligned access is taking place always_comb case(Funct3M[1:0]) - 2'b00: DataMisalignedM = 0; // lb, sb, lbu - 2'b01: DataMisalignedM = MemAdrM[0]; // lh, sh, lhu + 2'b00: DataMisalignedM = 0; // lb, sb, lbu + 2'b01: DataMisalignedM = MemAdrM[0]; // lh, sh, lhu 2'b10: DataMisalignedM = MemAdrM[1] | MemAdrM[0]; // lw, sw, flw, fsw, lwu - 2'b11: DataMisalignedM = |MemAdrM[2:0]; // ld, sd, fld, fsd + 2'b11: DataMisalignedM = |MemAdrM[2:0]; // ld, sd, fld, fsd endcase - // Squash unaligned data accesses + // Squash unaligned data accesses and failed store conditionals // *** this is also the place to squash if the cache is hit assign MemReadM = MemRWM[1] & ~DataMisalignedM; - assign MemWriteM = MemRWM[0] & ~DataMisalignedM; + assign MemWriteM = MemRWM[0] & ~DataMisalignedM && ~SquashSCM; // Determine if address is valid assign LoadMisalignedFaultM = DataMisalignedM & MemRWM[1]; @@ -72,6 +91,31 @@ module dmem ( assign StoreMisalignedFaultM = DataMisalignedM & MemRWM[0]; assign StoreAccessFaultM = DataAccessFaultM & MemRWM[0]; + // Handle atomic load reserved / store conditional + generate + if (`A_SUPPORTED) begin // atomic instructions supported + logic [`XLEN-1:2] ReservationPAdrW; + logic ReservationValidM, ReservationValidW; + logic lrM, scM, WriteAdrMatchM; + + assign lrM = MemReadM && AtomicM; + assign scM = MemRWM[0] && AtomicM; + assign WriteAdrMatchM = MemRWM[0] && (MemPAdrM == ReservationPAdrW) && ReservationValidW; + assign SquashSCM = scM && ~WriteAdrMatchM; + always_comb begin // ReservationValidM (next valiue of valid reservation) + if (lrM) ReservationValidM = 1; // set valid on load reserve + else if (scM || WriteAdrMatchM) ReservationValidM = 0; // clear valid on store to same address or any sc + else ReservationValidM = ReservationValidW; // otherwise don't change valid + end + flopenrc #(`XLEN-2) resadrreg(clk, reset, FlushW, ~StallW && lrM, MemPAdrM[`XLEN-1:2], ReservationPAdrW); // could drop clear on this one but not valid + flopenrc #(1) resvldreg(clk, reset, FlushW, ~StallW, ReservationValidM, ReservationValidW); + flopenrc #(1) squashreg(clk, reset, FlushW, ~StallW, SquashSCM, SquashSCW); + end else begin // Atomic operations not supported + assign SquashSCM = 0; + assign SquashSCW = 0; + end + endgenerate + // Data stall //assign DataStall = 0; diff --git a/wally-pipelined/src/generic/mux.sv b/wally-pipelined/src/generic/mux.sv index afa0a125..6621662d 100644 --- a/wally-pipelined/src/generic/mux.sv +++ b/wally-pipelined/src/generic/mux.sv @@ -59,4 +59,12 @@ module mux5 #(parameter WIDTH = 8) ( assign y = s[2] ? d4 : (s[1] ? (s[0] ? d3 : d2) : (s[0] ? d1 : d0)); endmodule +module mux6 #(parameter WIDTH = 8) ( + input logic [WIDTH-1:0] d0, d1, d2, d3, d4, d5, + input logic [2:0] s, + output logic [WIDTH-1:0] y); + + assign y = s[2] ? (s[0] ? d5 : d4) : (s[1] ? (s[0] ? d3 : d2) : (s[0] ? d1 : d0)); +endmodule + /* verilator lint_on DECLFILENAME */ diff --git a/wally-pipelined/src/ieu/controller.sv b/wally-pipelined/src/ieu/controller.sv index a917bbb6..663a24c5 100644 --- a/wally-pipelined/src/ieu/controller.sv +++ b/wally-pipelined/src/ieu/controller.sv @@ -47,6 +47,7 @@ module controller( input logic StallM, FlushM, output logic [1:0] MemRWM, output logic CSRReadM, CSRWriteM, PrivilegedM, + output logic AtomicM, output logic [2:0] Funct3M, output logic RegWriteM, // for Hazard Unit // Writeback stage control signals @@ -75,10 +76,11 @@ module controller( logic TargetSrcD, W64D, MulDivD; logic CSRZeroSrcD; logic CSRReadD; + logic AtomicD, AtomicE; logic CSRWriteD, CSRWriteE; logic InstrValidE, InstrValidM; logic PrivilegedD, PrivilegedE; - logic [20:0] ControlsD; + logic [21:0] ControlsD; logic aluc3D; logic subD, sraD, sltD, sltuD; logic BranchTakenE; @@ -97,38 +99,48 @@ module controller( generate always_comb case(OpD) - // RegWrite_ImmSrc_ALUSrc_MemRW_ResultSrc_Branch_ALUOp_Jump_TargetSrc_W64_CSRRead_Privileged_MulDiv_Illegal - 7'b0000011: ControlsD = 21'b1_000_01_10_001_0_00_0_0_0_0_0_0_0; // lw - 7'b0100011: ControlsD = 21'b0_001_01_01_000_0_00_0_0_0_0_0_0_0; // sw - 7'b0110011: if (Funct7D == 7'b0000000 || Funct7D == 7'b0100000) - ControlsD = 21'b1_000_00_00_000_0_10_0_0_0_0_0_0_0; // R-type - else if (Funct7D == 7'b0000001 && `M_SUPPORTED) - ControlsD = 21'b1_000_00_00_100_0_00_0_0_0_0_0_1_0; // Multiply/Divide - else - ControlsD = 21'b0_000_00_00_000_0_00_0_0_0_0_0_0_1; // non-implemented instruction - 7'b0111011: if ((Funct7D == 7'b0000000 || Funct7D == 7'b0100000) && `XLEN == 64) - ControlsD = 21'b1_000_00_00_000_0_10_0_0_1_0_0_0_0; // R-type W instructions for RV64i - else if (Funct7D == 7'b0000001 && `M_SUPPORTED && `XLEN == 64) - ControlsD = 21'b1_000_00_00_100_0_00_0_0_1_0_0_1_0; // W-type Multiply/Divide - else - ControlsD = 21'b0_000_00_00_000_0_00_0_0_0_0_0_0_1; // non-implemented instruction - 7'b1100011: ControlsD = 21'b0_010_00_00_000_1_01_0_0_0_0_0_0_0; // beq - 7'b0010011: ControlsD = 21'b1_000_01_00_000_0_10_0_0_0_0_0_0_0; // I-type ALU + // *** Atomic p. 132 assembly encodings, defs 48 + // RegWrite_ImmSrc_ALUSrc_MemRW_ResultSrc_Branch_ALUOp_Jump_TargetSrc_W64_CSRRead_Privileged_MulDiv_Atomic_Illegal + 7'b0000000: ControlsD = 22'b0_000_00_00_000_0_00_0_0_0_0_0_0_0_1; // illegal instruction + 7'b0000011: ControlsD = 22'b1_000_01_10_001_0_00_0_0_0_0_0_0_0_0; // lw + 7'b0001111: ControlsD = 22'b0_000_00_00_000_0_00_0_0_0_0_0_0_0_0; // fence = nop + 7'b0010011: ControlsD = 22'b1_000_01_00_000_0_10_0_0_0_0_0_0_0_0; // I-type ALU + 7'b0010111: ControlsD = 22'b1_100_11_00_000_0_00_0_0_0_0_0_0_0_0; // auipc 7'b0011011: if (`XLEN == 64) - ControlsD = 21'b1_000_01_00_000_0_10_0_0_1_0_0_0_0; // IW-type ALU for RV64i + ControlsD = 22'b1_000_01_00_000_0_10_0_0_1_0_0_0_0_0; // IW-type ALU for RV64i else - ControlsD = 21'b0_000_00_00_000_0_00_0_0_0_0_0_0_1; // non-implemented instruction - 7'b1101111: ControlsD = 21'b1_011_00_00_010_0_00_1_0_0_0_0_0_0; // jal - 7'b1100111: ControlsD = 21'b1_000_00_00_010_0_00_1_1_0_0_0_0_0; // jalr - 7'b0010111: ControlsD = 21'b1_100_11_00_000_0_00_0_0_0_0_0_0_0; // auipc - 7'b0110111: ControlsD = 21'b1_100_01_00_000_0_11_0_0_0_0_0_0_0; // lui - 7'b0001111: ControlsD = 21'b0_000_00_00_000_0_00_0_0_0_0_0_0_0; // fence = nop + ControlsD = 22'b0_000_00_00_000_0_00_0_0_0_0_0_0_0_1; // non-implemented instruction + 7'b0100011: ControlsD = 22'b0_001_01_01_000_0_00_0_0_0_0_0_0_0_0; // sw + 7'b0101111: if (`A_SUPPORTED) begin + if (InstrD[31:27] == 5'b00010) + ControlsD = 22'b1_000_00_10_001_0_00_0_0_0_0_0_0_1_0; // lr + else if (InstrD[31:27] == 5'b00011) + ControlsD = 22'b1_101_01_01_110_0_00_0_0_0_0_0_0_1_0; // sc + else + ControlsD = 22'b0_000_00_00_000_0_00_0_0_0_0_0_0_1_0; // other atomic; decode later + end else + ControlsD = 22'b0_000_00_00_000_0_00_0_0_0_0_0_0_0_1; // non-implemented instruction + 7'b0110011: if (Funct7D == 7'b0000000 || Funct7D == 7'b0100000) + ControlsD = 22'b1_000_00_00_000_0_10_0_0_0_0_0_0_0_0; // R-type + else if (Funct7D == 7'b0000001 && `M_SUPPORTED) + ControlsD = 22'b1_000_00_00_100_0_00_0_0_0_0_0_1_0_0; // Multiply/Divide + else + ControlsD = 22'b0_000_00_00_000_0_00_0_0_0_0_0_0_0_1; // non-implemented instruction + 7'b0110111: ControlsD = 22'b1_100_01_00_000_0_11_0_0_0_0_0_0_0_0; // lui + 7'b0111011: if ((Funct7D == 7'b0000000 || Funct7D == 7'b0100000) && `XLEN == 64) + ControlsD = 22'b1_000_00_00_000_0_10_0_0_1_0_0_0_0_0; // R-type W instructions for RV64i + else if (Funct7D == 7'b0000001 && `M_SUPPORTED && `XLEN == 64) + ControlsD = 22'b1_000_00_00_100_0_00_0_0_1_0_0_1_0_0; // W-type Multiply/Divide + else + ControlsD = 22'b0_000_00_00_000_0_00_0_0_0_0_0_0_0_1; // non-implemented instruction + 7'b1100011: ControlsD = 22'b0_010_00_00_000_1_01_0_0_0_0_0_0_0_0; // beq + 7'b1100111: ControlsD = 22'b1_000_00_00_010_0_00_1_1_0_0_0_0_0_0; // jalr + 7'b1101111: ControlsD = 22'b1_011_00_00_010_0_00_1_0_0_0_0_0_0_0; // jal 7'b1110011: if (Funct3D == 3'b000) - ControlsD = 21'b0_000_00_00_000_0_00_0_0_0_0_1_0_0; // privileged; decoded further in priveleged modules + ControlsD = 22'b0_000_00_00_000_0_00_0_0_0_0_1_0_0_0; // privileged; decoded further in priveleged modules else - ControlsD = 21'b1_000_00_00_011_0_00_0_0_0_1_0_0_0; // csrs - 7'b0000000: ControlsD = 21'b0_000_00_00_000_0_00_0_0_0_0_0_0_1; // illegal instruction - default: ControlsD = 21'b0_000_00_00_000_0_00_0_0_0_0_0_0_1; // non-implemented instruction + ControlsD = 22'b1_000_00_00_011_0_00_0_0_0_1_0_0_0_0; // csrs + default: ControlsD = 22'b0_000_00_00_000_0_00_0_0_0_0_0_0_0_1; // non-implemented instruction endcase endgenerate @@ -137,7 +149,7 @@ module controller( assign IllegalBaseInstrFaultD = ControlsD[0]; assign {RegWriteD, ImmSrcD, ALUSrcAD, ALUSrcBD, MemRWD, ResultSrcD, BranchD, ALUOpD, JumpD, TargetSrcD, W64D, CSRReadD, - PrivilegedD, MulDivD, unused} = ControlsD & ~IllegalIEUInstrFaultD; + PrivilegedD, MulDivD, AtomicD, unused} = ControlsD & ~IllegalIEUInstrFaultD; // *** move Privileged, CSRwrite?? Or move controller out of IEU into datapath and handle all instructions assign CSRZeroSrcD = InstrD[14] ? (InstrD[19:15] == 0) : (Rs1D == 0); // Is a CSR instruction using zero as the source? @@ -160,9 +172,9 @@ module controller( endcase // Execute stage pipeline control register and logic - flopenrc #(25) controlregE(clk, reset, FlushE, ~StallE, - {RegWriteD, ResultSrcD, MemRWD, JumpD, BranchD, ALUControlD, ALUSrcAD, ALUSrcBD, TargetSrcD, CSRReadD, CSRWriteD, PrivilegedD, Funct3D, W64D, MulDivD, 1'b1}, - {RegWriteE, ResultSrcE, MemRWE, JumpE, BranchE, ALUControlE, ALUSrcAE, ALUSrcBE, TargetSrcE, CSRReadE, CSRWriteE, PrivilegedE, Funct3E, W64E, MulDivE, InstrValidE}); + flopenrc #(26) controlregE(clk, reset, FlushE, ~StallE, + {RegWriteD, ResultSrcD, MemRWD, JumpD, BranchD, ALUControlD, ALUSrcAD, ALUSrcBD, TargetSrcD, CSRReadD, CSRWriteD, PrivilegedD, Funct3D, W64D, MulDivD, AtomicD, 1'b1}, + {RegWriteE, ResultSrcE, MemRWE, JumpE, BranchE, ALUControlE, ALUSrcAE, ALUSrcBE, TargetSrcE, CSRReadE, CSRWriteE, PrivilegedE, Funct3E, W64E, MulDivE, AtomicE, InstrValidE}); // Branch Logic assign {zeroE, ltE, ltuE} = FlagsE; @@ -183,9 +195,9 @@ module controller( assign MemReadE = MemRWE[1]; // Memory stage pipeline control register - flopenrc #(13) controlregM(clk, reset, FlushM, ~StallM, - {RegWriteE, ResultSrcE, MemRWE, CSRReadE, CSRWriteE, PrivilegedE, Funct3E, InstrValidE}, - {RegWriteM, ResultSrcM, MemRWM, CSRReadM, CSRWriteM, PrivilegedM, Funct3M, InstrValidM}); + flopenrc #(14) controlregM(clk, reset, FlushM, ~StallM, + {RegWriteE, ResultSrcE, MemRWE, CSRReadE, CSRWriteE, PrivilegedE, Funct3E, AtomicE, InstrValidE}, + {RegWriteM, ResultSrcM, MemRWM, CSRReadM, CSRWriteM, PrivilegedM, Funct3M, AtomicM, InstrValidM}); // Writeback stage pipeline control register flopenrc #(5) controlregW(clk, reset, FlushW, ~StallW, diff --git a/wally-pipelined/src/ieu/datapath.sv b/wally-pipelined/src/ieu/datapath.sv index 022cc625..f8e356d3 100644 --- a/wally-pipelined/src/ieu/datapath.sv +++ b/wally-pipelined/src/ieu/datapath.sv @@ -47,9 +47,10 @@ module datapath ( // Writeback stage signals input logic StallW, FlushW, input logic RegWriteW, + input logic SquashSCW, input logic [2:0] ResultSrcW, input logic [`XLEN-1:0] PCLinkW, - input logic [`XLEN-1:0] CSRReadValW, ReadDataW, MulDivResultW, + input logic [`XLEN-1:0] CSRReadValW, ReadDataW, MulDivResultW, // Hazard Unit signals output logic [4:0] Rs1D, Rs2D, Rs1E, Rs2E, output logic [4:0] RdE, RdM, RdW @@ -70,6 +71,7 @@ module datapath ( // Memory stage signals logic [`XLEN-1:0] ALUResultM; // Writeback stage signals + logic [`XLEN-1:0] SCResultW; logic [`XLEN-1:0] ALUResultW; logic [`XLEN-1:0] ResultW; @@ -107,5 +109,13 @@ module datapath ( flopenrc #(`XLEN) ALUResultWReg(clk, reset, FlushW, ~StallW, ALUResultM, ALUResultW); flopenrc #(5) RdWEg(clk, reset, FlushW, ~StallW, RdM, RdW); - mux5 #(`XLEN) resultmux(ALUResultW, ReadDataW, PCLinkW, CSRReadValW, MulDivResultW, ResultSrcW, ResultW); + // handle Store Conditional result if atomic extension supported + generate + if (`A_SUPPORTED) + assign SCResultW = SquashSCW ? {{(`XLEN-1){1'b0}}, 1'b1} : {{(`XLEN-1){1'b0}}, 1'b0}; + else + assign SCResultW = 0; + endgenerate + + mux6 #(`XLEN) resultmux(ALUResultW, ReadDataW, PCLinkW, CSRReadValW, MulDivResultW, SCResultW, ResultSrcW, ResultW); endmodule diff --git a/wally-pipelined/src/ieu/extend.sv b/wally-pipelined/src/ieu/extend.sv index 243dced3..8d79b0a3 100644 --- a/wally-pipelined/src/ieu/extend.sv +++ b/wally-pipelined/src/ieu/extend.sv @@ -29,19 +29,26 @@ module extend ( input logic [31:7] InstrD, input logic [2:0] ImmSrcD, output logic [`XLEN-1:0 ] ExtImmD); + + logic [`XLEN-1:0] undefined = {(`XLEN){1'bx}}; // could change to 0 after debug - always_comb - case(ImmSrcD) - // I-type - 3'b000: ExtImmD = {{(`XLEN-12){InstrD[31]}}, InstrD[31:20]}; - // S-type (stores) - 3'b001: ExtImmD = {{(`XLEN-12){InstrD[31]}}, InstrD[31:25], InstrD[11:7]}; - // B-type (branches) - 3'b010: ExtImmD = {{(`XLEN-12){InstrD[31]}}, InstrD[7], InstrD[30:25], InstrD[11:8], 1'b0}; - // J-type (jal) - 3'b011: ExtImmD = {{(`XLEN-20){InstrD[31]}}, InstrD[19:12], InstrD[20], InstrD[30:21], 1'b0}; - // U-type (lui, auipc) - 3'b100: ExtImmD = {{(`XLEN-31){InstrD[31]}}, InstrD[30:12], 12'b0}; - default: ExtImmD = {(`XLEN-1){1'bx}}; // undefined - endcase + generate + always_comb + case(ImmSrcD) + // I-type + 3'b000: ExtImmD = {{(`XLEN-12){InstrD[31]}}, InstrD[31:20]}; + // S-type (stores) + 3'b001: ExtImmD = {{(`XLEN-12){InstrD[31]}}, InstrD[31:25], InstrD[11:7]}; + // B-type (branches) + 3'b010: ExtImmD = {{(`XLEN-12){InstrD[31]}}, InstrD[7], InstrD[30:25], InstrD[11:8], 1'b0}; + // J-type (jal) + 3'b011: ExtImmD = {{(`XLEN-20){InstrD[31]}}, InstrD[19:12], InstrD[20], InstrD[30:21], 1'b0}; + // U-type (lui, auipc) + 3'b100: ExtImmD = {{(`XLEN-31){InstrD[31]}}, InstrD[30:12], 12'b0}; + // Store Conditional: zero offset + 3'b101: if (`A_SUPPORTED) ExtImmD = 0; + else ExtImmD = undefined; + default: ExtImmD = undefined; // undefined + endcase + endgenerate endmodule diff --git a/wally-pipelined/src/ieu/ieu.sv b/wally-pipelined/src/ieu/ieu.sv index 29ffb497..34787236 100644 --- a/wally-pipelined/src/ieu/ieu.sv +++ b/wally-pipelined/src/ieu/ieu.sv @@ -40,7 +40,9 @@ module ieu ( // Memory stage interface input logic DataMisalignedM, input logic DataAccessFaultM, + input logic SquashSCW, output logic [1:0] MemRWM, + output logic AtomicM, output logic [`XLEN-1:0] MemAdrM, WriteDataM, output logic [`XLEN-1:0] SrcAM, output logic [2:0] Funct3M, diff --git a/wally-pipelined/src/ifu/ifu.sv b/wally-pipelined/src/ifu/ifu.sv index 1cddbe6d..88e4f0be 100644 --- a/wally-pipelined/src/ifu/ifu.sv +++ b/wally-pipelined/src/ifu/ifu.sv @@ -52,6 +52,12 @@ module ifu ( output logic IllegalIEUInstrFaultD, output logic InstrMisalignedFaultM, output logic [`XLEN-1:0] InstrMisalignedAdrM, + // TLB management + //input logic [`XLEN-1:0] PageTableEntryF, + //input logic ITLBWriteF, ITLBFlushF, + // *** satp value will come from CSRs + // input logic [`XLEN-1:0] SATP, + output logic ITLBMissF, ITLBHitF, // bogus input logic [15:0] rd2 ); @@ -65,8 +71,18 @@ module ifu ( logic [31:0] InstrF, InstrRawD, InstrE, InstrW; logic [31:0] nop = 32'h00000013; // instruction for NOP + // *** temporary hack until we can figure out how to get actual satp value + // from priv unit -- Thomas F + logic [`XLEN-1:0] SATP = '0; + // *** temporary hack until walker is hooked up -- Thomas F + logic [`XLEN-1:0] PageTableEntryF = '0; + logic ITLBFlushF = '0; + logic ITLBWriteF = '0; + tlb #(3) itlb(clk, reset, SATP, PCF, PageTableEntryF, ITLBWriteF, ITLBFlushF, + InstrPAdrF, ITLBMissF, ITLBHitF); + // *** put memory interface on here, InstrF becomes output - assign InstrPAdrF = PCF; // *** no MMU + //assign InstrPAdrF = PCF; // *** no MMU //assign InstrReadF = ~StallD; // *** & ICacheMissF; add later assign InstrReadF = 1; // *** & ICacheMissF; add later diff --git a/wally-pipelined/src/mmu/tlb.sv b/wally-pipelined/src/mmu/tlb.sv new file mode 100644 index 00000000..7be0f0b7 --- /dev/null +++ b/wally-pipelined/src/mmu/tlb.sv @@ -0,0 +1,234 @@ +/////////////////////////////////////////// +// tlb_toy.sv +// +// Written: jtorrey@hmc.edu 16 February 2021 +// Modified: +// +// Purpose: Example translation lookaside buffer +// Cache of virtural-to-physical address translations +// +// 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" + +/** + * sv32 specs + * ---------- + * Virtual address [31:0] (32 bits) + * [________________________________] + * |--VPN1--||--VPN0--||----OFF---| + * 10 10 12 + * + * Physical address [33:0] (34 bits) + * [__________________________________] + * |---PPN1---||--PPN0--||----OFF---| + * 12 10 12 + * + * Page Table Entry [31:0] (32 bits) + * [________________________________] + * |---PPN1---||--PPN0--|||DAGUXWRV + * 12 10 ^^ + * RSW(2) -- for OS + */ + +/* *** TODO: + * - add LRU algorithm (select the write index based on which entry was used + * least recently) + * - refactor modules into multiple files + */ + +// The TLB will have 2**ENTRY_BITS total entries +module tlb #(parameter ENTRY_BITS = 3) ( + input clk, reset, + + // Current value of satp CSR (from privileged unit) + input [`XLEN-1:0] SATP, // *** How do we get this? + + // Virtual address input + input [`XLEN-1:0] VirtualAddress, + + // Controls for writing a new entry to the TLB + input [`XLEN-1:0] PageTableEntryWrite, + input TLBWrite, + + // Invalidate all TLB entries + input TLBFlush, + + // Physical address outputs + output [`XLEN-1:0] PhysicalAddress, + output TLBMiss, + output TLBHit +); + + generate + if (`XLEN == 32) begin: ARCH + localparam VPN_BITS = 20; + localparam PPN_BITS = 22; + localparam PA_BITS = 34; + + logic SvMode; + assign SvMode = SATP[31]; // *** change to an enum somehow? + end else begin: ARCH + localparam VPN_BITS = 27; + localparam PPN_BITS = 44; + localparam PA_BITS = 56; + + logic SvMode; // currently just a boolean whether translation enabled + assign SvMode = SATP[63]; // *** change to an enum somehow? + end + endgenerate + + // Index (currently random) to write the next TLB entry + logic [ENTRY_BITS-1:0] WriteIndex; + + // Sections of the virtual and physical addresses + logic [ARCH.VPN_BITS-1:0] VirtualPageNumber; + logic [ARCH.PPN_BITS-1:0] PhysicalPageNumber; + logic [11:0] PageOffset; + logic [ARCH.PA_BITS-1:0] PhysicalAddressFull; + + // Pattern and pattern location in the CAM + logic [ENTRY_BITS-1:0] VPNIndex; + + // RAM access location + logic [ENTRY_BITS-1:0] EntryIndex; + + // Page table entry matching the virtual address + logic [`XLEN-1:0] PageTableEntry; + + assign VirtualPageNumber = VirtualAddress[ARCH.VPN_BITS+11:12]; + assign PageOffset = VirtualAddress[11:0]; + + // Choose a read or write location to the entry list + mux2 #(3) indexmux(VPNIndex, WriteIndex, TLBWrite, EntryIndex); + + // Currently use random replacement algorithm + tlb_rand rdm(.*); + + tlb_ram #(ENTRY_BITS) ram(.*); + tlb_cam #(ENTRY_BITS, ARCH.VPN_BITS) cam(.*); + + always_comb begin + assign PhysicalPageNumber = PageTableEntry[ARCH.PPN_BITS+9:10]; + + if (TLBHit) begin + assign PhysicalAddressFull = {PhysicalPageNumber, PageOffset}; + end else begin + assign PhysicalAddressFull = 8'b0; // *** Actual behavior; disabled until walker functioning + //assign PhysicalAddressFull = {2'b0, VirtualPageNumber, PageOffset} // *** pass through should be removed as soon as walker ready + end + end + + generate + if (`XLEN == 32) begin + mux2 #(`XLEN) addressmux(VirtualAddress, PhysicalAddressFull[31:0], ARCH.SvMode, PhysicalAddress); + end else begin + mux2 #(`XLEN) addressmux(VirtualAddress, {8'b0, PhysicalAddressFull}, ARCH.SvMode, PhysicalAddress); + end + endgenerate + + assign TLBMiss = ~TLBHit & ~(TLBWrite | TLBFlush) & ARCH.SvMode; +endmodule + +module tlb_ram #(parameter ENTRY_BITS = 3) ( + input clk, reset, + input [ENTRY_BITS-1:0] EntryIndex, + input [`XLEN-1:0] PageTableEntryWrite, + input TLBWrite, + + output [`XLEN-1:0] PageTableEntry +); + + localparam NENTRIES = 2**ENTRY_BITS; + + logic [`XLEN-1:0] ram [0:NENTRIES-1]; + always @(posedge clk) begin + if (TLBWrite) ram[EntryIndex] <= PageTableEntryWrite; + end + + assign PageTableEntry = ram[EntryIndex]; + + initial begin + for (int i = 0; i < NENTRIES; i++) + ram[i] = `XLEN'b0; + end + +endmodule + +module tlb_cam #(parameter ENTRY_BITS = 3, + parameter KEY_BITS = 20) ( + input clk, reset, + input [KEY_BITS-1:0] VirtualPageNumber, + input [ENTRY_BITS-1:0] WriteIndex, + input TLBWrite, + input TLBFlush, + output [ENTRY_BITS-1:0] VPNIndex, + output TLBHit +); + + localparam NENTRIES = 2**ENTRY_BITS; + + // Each entry of this memory has KEY_BITS for the key plus one valid bit. + logic [KEY_BITS:0] ram [0:NENTRIES-1]; + + logic [ENTRY_BITS-1:0] matched_address_comb; + logic match_found_comb; + + always @(posedge clk) begin + if (TLBWrite) ram[WriteIndex] <= {1'b1,VirtualPageNumber}; + if (TLBFlush) begin + for (int i = 0; i < NENTRIES; i++) + ram[i][KEY_BITS] = 1'b0; // Zero out msb (valid bit) of all entries + end + end + + // *** Check whether this for loop synthesizes correctly + always_comb begin + match_found_comb = 1'b0; + matched_address_comb = '0; + for (int i = 0; i < NENTRIES; i++) begin + if (ram[i] == {1'b1,VirtualPageNumber} && !match_found_comb) begin + matched_address_comb = i; + match_found_comb = 1; + end else begin + matched_address_comb = matched_address_comb; + match_found_comb = match_found_comb; + end + end + end + + assign VPNIndex = matched_address_comb; + assign TLBHit = match_found_comb & ~(TLBWrite | TLBFlush); + + initial begin + for (int i = 0; i < NENTRIES; i++) + ram[i] <= '0; + end + +endmodule + +module tlb_rand #(parameter ENTRY_BITS = 3) ( + input clk, reset, + output [ENTRY_BITS-1:0] WriteIndex +); + + logic [31:0] data; + assign data = $urandom; + assign WriteIndex = data[ENTRY_BITS:0]; + +endmodule diff --git a/wally-pipelined/src/tlb_toy/tlb_testbench.sv b/wally-pipelined/src/tlb_toy/tlb_testbench.sv index add65d3d..4aa30542 100644 --- a/wally-pipelined/src/tlb_toy/tlb_testbench.sv +++ b/wally-pipelined/src/tlb_toy/tlb_testbench.sv @@ -1,20 +1,25 @@ -module testbench(); +`include "wally-config.vh" + +module tlb_testbench(); logic clk, reset; // DUT inputs - logic [31:0] PCF; - logic [31:0] PageTableEntryF; - logic ITLBWriteF, ITLBFlushF; + logic [`XLEN-1:0] SATP; + logic [`XLEN-1:0] VirtualAddress; + logic [`XLEN-1:0] PageTableEntryWrite; + logic TLBWrite, TLBFlush; // DUT outputs - logic [31:0] PCPF; - logic ITLBMissF, ITLBHitF; + logic [`XLEN-1:0] PhysicalAddress; + logic TLBMiss, TLBHit; // Testbench signals logic [33:0] expected; logic [31:0] vectornum, errors; logic [99:0] testvectors[10000:0]; + assign SATP = {1'b1, 31'b0}; + // instantiate device under test tlb_toy dut(.*); @@ -31,17 +36,17 @@ module testbench(); // apply test vectors on rising edge of clk always @(posedge clk) begin - #1; {PCF, PageTableEntryF, ITLBWriteF, ITLBFlushF, expected} = testvectors[vectornum]; + #1; {VirtualAddress, PageTableEntryWrite, TLBWrite, TLBFlush, expected} = testvectors[vectornum]; end // check results on falling edge of clk always @(negedge clk) if (~reset) begin // skip during reset - if ({PCPF, ITLBMissF, ITLBHitF} !== expected) begin // check result - $display("Error: PCF = %b, write = %b, data = %b, flush = %b", PCF, - ITLBWriteF, PageTableEntryF, ITLBFlushF); + if ({PhysicalAddress, TLBMiss, TLBHit} !== expected) begin // check result + $display("Error: VirtualAddress = %b, write = %b, data = %b, flush = %b", VirtualAddress, + TLBWrite, PageTableEntryWrite, TLBFlush); $display(" outputs = %b %b %b (%b expected)", - PCPF, ITLBMissF, ITLBHitF, expected); + PhysicalAddress, TLBMiss, TLBHit, expected); errors = errors + 1; end vectornum = vectornum + 1; diff --git a/wally-pipelined/src/tlb_toy/tlb_toy.sv b/wally-pipelined/src/tlb_toy/tlb_toy.sv deleted file mode 100644 index 43c94bab..00000000 --- a/wally-pipelined/src/tlb_toy/tlb_toy.sv +++ /dev/null @@ -1,208 +0,0 @@ -/////////////////////////////////////////// -// tlb_toy.sv -// -// Written: jtorrey@hmc.edu 16 February 2021 -// Modified: -// -// Purpose: Example translation lookaside buffer -// Cache of virtural-to-physical address translations -// -// 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" - -/** - * sv32 specs - * ---------- - * Virtual address [31:0] (32 bits) - * [________________________________] - * |--VPN1--||--VPN0--||----OFF---| - * 10 10 12 - * - * Physical address [33:0] (34 bits) - * [__________________________________] - * |---PPN1---||--PPN0--||----OFF---| - * 12 10 12 - * - * Page Table Entry [31:0] (32 bits) - * [________________________________] - * |---PPN1---||--PPN0--|||DAGUXWRV - * 12 10 ^^ - * RSW(2) -- for OS - */ - -/* *** TODO: - * - add LRU algorithm (select the write index based on which entry was used - * least recently) - * - rename signals to use .* notation in CAM and RAM - */ - -module tlb_toy ( - input clk, reset, - - // Virtual address input - input [31:0] PCF, - - // Controls for writing a new entry to the TLB - input [31:0] PageTableEntryF, - input ITLBWriteF, - - // Invalidate all TLB entries - input ITLBFlushF, - - // Physical address outputs - output [31:0] PCPF, - output ITLBMissF, - output ITLBHitF -); - // Index (currently random) to write the next TLB entry - logic [2:0] WriteIndexF; - - // Sections of the virtual and physical addresses - logic [19:0] VirtualPageNumberF; - logic [21:0] PhysicalPageNumberF; - logic [11:0] PageOffsetF; - logic [33:0] PhysicalAddressF; - - // Pattern and pattern location in the CAM - logic [2:0] VPNIndexF; - - // RAM access location - logic [2:0] ITLBEntryIndex; - - // Page table entry matching the virtual address - logic [31:0] PTEMatchF; - - assign VirtualPageNumberF = PCF[31:12]; - assign PageOffsetF = PCF[11:0]; - - // Choose a read or write location to the entry list - mux2 #(3) indexmux(VPNIndexF, WriteIndexF, ITLBWriteF, ITLBEntryIndex); - - // Currently use random replacement algorithm - rand3 rdm(clk, reset, WriteIndexF); - - ram8x32 ram(clk, reset, ITLBEntryIndex, PageTableEntryF, ITLBWriteF, PTEMatchF); - cam8x21 cam(clk, reset, ITLBWriteF, VirtualPageNumberF, WriteIndexF, - ITLBFlushF, VPNIndexF, ITLBHitF); - - always_comb begin - assign PhysicalPageNumberF = PTEMatchF[31:10]; - - if (ITLBHitF) begin - assign PhysicalAddressF = {PhysicalPageNumberF, PageOffsetF}; - end else begin - assign PhysicalAddressF = 34'b0; - end - end - - assign PCPF = PhysicalAddressF[31:0]; - assign ITLBMissF = ~ITLBHitF & ~(ITLBWriteF | ITLBFlushF); - -endmodule - -// *** Add parameter for number of tlb lines (currently 8) -module ram8x32 ( - input clk, reset, - input [2:0] address, - input [31:0] data, - input we, - - output [31:0] out_data -); - - logic [31:0] ram [0:7]; - always @(posedge clk) begin - if (we) ram[address] <= data; - end - - assign out_data = ram[address]; - - initial begin - for (int i = 0; i < 8; i++) - ram[i] = 32'h0; - end - -endmodule - -module cam8x21 ( - input clk, reset, we, - input [19:0] pattern, - input [2:0] write_address, - input ITLBFlushF, - output [2:0] matched_address, - output match_found -); - - logic [20:0] ram [0:7]; - logic [7:0] match_line; - - logic [2:0] matched_address_comb; - logic match_found_comb; - - always @(posedge clk) begin - if (we) ram[write_address] <= {1'b1,pattern}; - if (ITLBFlushF) begin - for (int i = 0; i < 8; i++) - ram[i][20] = 1'b0; - end - end - - // *** Check whether this for loop synthesizes correctly - always_comb begin - match_found_comb = 1'b0; - matched_address_comb = 3'b0; - for (int i = 0; i < 8; i++) begin - if (ram[i] == {1'b1,pattern} && !match_found_comb) begin - matched_address_comb = i; - match_found_comb = 1; - end else begin - matched_address_comb = matched_address_comb; - match_found_comb = match_found_comb; - end - end - end - - assign matched_address = matched_address_comb; - assign match_found = match_found_comb & ~(we | ITLBFlushF); - - initial begin - for (int i = 0; i < 8; i++) - ram[i] <= 0; - end - -endmodule - -module mux2 #(parameter WIDTH = 8) ( - input logic [WIDTH-1:0] d0, d1, - input logic s, - output logic [WIDTH-1:0] y); - - assign y = s ? d1 : d0; -endmodule - -module rand3 ( - input clk, reset, - output [2:0] WriteIndexF -); - - logic [31:0] data; - assign data = $urandom; - assign WriteIndexF = data[2:0]; - -endmodule diff --git a/wally-pipelined/src/tlb_toy/tlb_toy.sv.OLD b/wally-pipelined/src/tlb_toy/tlb_toy.sv.OLD new file mode 100644 index 00000000..cafc15fa --- /dev/null +++ b/wally-pipelined/src/tlb_toy/tlb_toy.sv.OLD @@ -0,0 +1,233 @@ +/////////////////////////////////////////// +// tlb_toy.sv +// +// Written: jtorrey@hmc.edu 16 February 2021 +// Modified: +// +// Purpose: Example translation lookaside buffer +// Cache of virtural-to-physical address translations +// +// 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" + +/** + * sv32 specs + * ---------- + * Virtual address [31:0] (32 bits) + * [________________________________] + * |--VPN1--||--VPN0--||----OFF---| + * 10 10 12 + * + * Physical address [33:0] (34 bits) + * [__________________________________] + * |---PPN1---||--PPN0--||----OFF---| + * 12 10 12 + * + * Page Table Entry [31:0] (32 bits) + * [________________________________] + * |---PPN1---||--PPN0--|||DAGUXWRV + * 12 10 ^^ + * RSW(2) -- for OS + */ + +/* *** TODO: + * - add LRU algorithm (select the write index based on which entry was used + * least recently) + */ + +// The TLB will have 2**ENTRY_BITS total entries +module tlb_toy #(parameter ENTRY_BITS = 3) ( + input clk, reset, + + // Current value of satp CSR (from privileged unit) + input [`XLEN-1:0] SATP, // *** How do we get this? + + // Virtual address input + input [`XLEN-1:0] VirtualAddress, + + // Controls for writing a new entry to the TLB + input [`XLEN-1:0] PageTableEntryWrite, + input TLBWrite, + + // Invalidate all TLB entries + input TLBFlush, + + // Physical address outputs + output [`XLEN-1:0] PhysicalAddress, + output TLBMiss, + output TLBHit +); + + generate + if (`XLEN == 32) begin: ARCH + localparam VPN_BITS = 20; + localparam PPN_BITS = 22; + localparam PA_BITS = 34; + + logic SvMode; + assign SvMode = SATP[31]; // *** change to an enum somehow? + end else begin: ARCH + localparam VPN_BITS = 27; + localparam PPN_BITS = 44; + localparam PA_BITS = 56; + + logic SvMode; // currently just a boolean whether translation enabled + assign SvMode = SATP[63]; // *** change to an enum somehow? + end + endgenerate + + // Index (currently random) to write the next TLB entry + logic [ENTRY_BITS-1:0] WriteIndex; + + // Sections of the virtual and physical addresses + logic [ARCH.VPN_BITS-1:0] VirtualPageNumber; + logic [ARCH.PPN_BITS-1:0] PhysicalPageNumber; + logic [11:0] PageOffset; + logic [ARCH.PA_BITS-1:0] PhysicalAddressFull; + + // Pattern and pattern location in the CAM + logic [ENTRY_BITS-1:0] VPNIndex; + + // RAM access location + logic [ENTRY_BITS-1:0] EntryIndex; + + // Page table entry matching the virtual address + logic [`XLEN-1:0] PageTableEntry; + + assign VirtualPageNumber = VirtualAddress[ARCH.VPN_BITS+11:12]; + assign PageOffset = VirtualAddress[11:0]; + + // Choose a read or write location to the entry list + mux2 #(3) indexmux(VPNIndex, WriteIndex, TLBWrite, EntryIndex); + + // Currently use random replacement algorithm + tlb_rand rdm(.*); + + tlb_ram #(ENTRY_BITS) ram(.*); + tlb_cam #(ENTRY_BITS, ARCH.VPN_BITS) cam(.*); + + always_comb begin + assign PhysicalPageNumber = PageTableEntry[ARCH.PPN_BITS+9:10]; + + if (TLBHit) begin + assign PhysicalAddressFull = {PhysicalPageNumber, PageOffset}; + end else begin + assign PhysicalAddressFull = 8'b0; // *** Actual behavior; disabled until walker functioning + //assign PhysicalAddressFull = {2'b0, VirtualPageNumber, PageOffset} // *** pass through should be removed as soon as walker ready + end + end + + generate + if (`XLEN == 32) begin + mux2 #(`XLEN) addressmux(VirtualAddress, PhysicalAddressFull[31:0], ARCH.SvMode, PhysicalAddress); + end else begin + mux2 #(`XLEN) addressmux(VirtualAddress, {8'b0, PhysicalAddressFull}, ARCH.SvMode, PhysicalAddress); + end + endgenerate + + assign TLBMiss = ~TLBHit & ~(TLBWrite | TLBFlush) & ARCH.SvMode; +endmodule + +module tlb_ram #(parameter ENTRY_BITS = 3) ( + input clk, reset, + input [ENTRY_BITS-1:0] EntryIndex, + input [`XLEN-1:0] PageTableEntryWrite, + input TLBWrite, + + output [`XLEN-1:0] PageTableEntry +); + + localparam NENTRIES = 2**ENTRY_BITS; + + logic [`XLEN-1:0] ram [0:NENTRIES-1]; + always @(posedge clk) begin + if (TLBWrite) ram[EntryIndex] <= PageTableEntryWrite; + end + + assign PageTableEntry = ram[EntryIndex]; + + initial begin + for (int i = 0; i < NENTRIES; i++) + ram[i] = `XLEN'b0; + end + +endmodule + +module tlb_cam #(parameter ENTRY_BITS = 3, + parameter KEY_BITS = 20) ( + input clk, reset, + input [KEY_BITS-1:0] VirtualPageNumber, + input [ENTRY_BITS-1:0] WriteIndex, + input TLBWrite, + input TLBFlush, + output [ENTRY_BITS-1:0] VPNIndex, + output TLBHit +); + + localparam NENTRIES = 2**ENTRY_BITS; + + // Each entry of this memory has KEY_BITS for the key plus one valid bit. + logic [KEY_BITS:0] ram [0:NENTRIES-1]; + + logic [ENTRY_BITS-1:0] matched_address_comb; + logic match_found_comb; + + always @(posedge clk) begin + if (TLBWrite) ram[WriteIndex] <= {1'b1,VirtualPageNumber}; + if (TLBFlush) begin + for (int i = 0; i < NENTRIES; i++) + ram[i][KEY_BITS] = 1'b0; // Zero out msb (valid bit) of all entries + end + end + + // *** Check whether this for loop synthesizes correctly + always_comb begin + match_found_comb = 1'b0; + matched_address_comb = '0; + for (int i = 0; i < NENTRIES; i++) begin + if (ram[i] == {1'b1,VirtualPageNumber} && !match_found_comb) begin + matched_address_comb = i; + match_found_comb = 1; + end else begin + matched_address_comb = matched_address_comb; + match_found_comb = match_found_comb; + end + end + end + + assign VPNIndex = matched_address_comb; + assign TLBHit = match_found_comb & ~(TLBWrite | TLBFlush); + + initial begin + for (int i = 0; i < NENTRIES; i++) + ram[i] <= '0; + end + +endmodule + +module tlb_rand #(parameter ENTRY_BITS = 3) ( + input clk, reset, + output [ENTRY_BITS:0] WriteIndex +); + + logic [31:0] data; + assign data = $urandom; + assign WriteIndex = data[ENTRY_BITS:0]; + +endmodule diff --git a/wally-pipelined/src/uncore/imem.sv b/wally-pipelined/src/uncore/imem.sv index 2711277e..239070e3 100644 --- a/wally-pipelined/src/uncore/imem.sv +++ b/wally-pipelined/src/uncore/imem.sv @@ -56,20 +56,12 @@ module imem ( generate if (`XLEN==32) begin assign InstrF = AdrF[1] ? {rd2[15:0], rd[31:16]} : rd; - if(`TIMBASE==0) begin - assign InstrAccessFaultF = 0; - end else begin - assign InstrAccessFaultF = ~AdrF[31] | (|AdrF[30:16]); // memory mapped to 0x80000000-0x8000FFFF - end + assign InstrAccessFaultF = ~&(({AdrF,1'b0} ~^ `TIMBASE) | `TIMRANGE); end else begin assign InstrF = AdrF[2] ? (AdrF[1] ? {rd2[15:0], rd[63:48]} : rd[63:32]) : (AdrF[1] ? rd[47:16] : rd[31:0]); - /*if(`TIMBASE==0) begin - assign InstrAccessFaultF = 0; - end else begin - assign InstrAccessFaultF = (|AdrF[`XLEN-1:32]) | ~AdrF[31] | (|AdrF[30:16]); // memory mapped to 0x80000000-0x8000FFFF] - end*/ - assign InstrAccessFaultF = 0; //busybear: for now, i know we're not doing this + //assign InstrAccessFaultF = 0; //busybear: for now, i know we're not doing this + assign InstrAccessFaultF = |AdrF[`XLEN-1:32] | ~&({AdrF[31:1],1'b0} ~^ `TIMBASE | `TIMRANGE); end endgenerate endmodule diff --git a/wally-pipelined/src/wally/wallypipelinedhart.sv b/wally-pipelined/src/wally/wallypipelinedhart.sv index 793eafb2..ded4df3d 100644 --- a/wally-pipelined/src/wally/wallypipelinedhart.sv +++ b/wally-pipelined/src/wally/wallypipelinedhart.sv @@ -60,7 +60,7 @@ module wallypipelinedhart ( // new signals that must connect through DP logic MulDivE, W64E; - logic CSRReadM, CSRWriteM, PrivilegedM; + logic CSRReadM, CSRWriteM, PrivilegedM, AtomicM; logic [`XLEN-1:0] SrcAE, SrcBE; logic [`XLEN-1:0] SrcAM; logic [2:0] Funct3E; @@ -85,6 +85,11 @@ module wallypipelinedhart ( logic [4:0] SetFflagsM; logic [2:0] FRM_REGW; logic FloatRegWriteW; + logic SquashSCW; + + // memory management unit signals + logic ITLBMissF, ITLBHitF; + logic DTLBMissM, DTLBHitM; // bus interface to dmem logic MemReadM, MemWriteM; diff --git a/wally-pipelined/testbench/testbench-coremark.sv b/wally-pipelined/testbench/testbench-coremark.sv index 4aef5e40..9f458567 100644 --- a/wally-pipelined/testbench/testbench-coremark.sv +++ b/wally-pipelined/testbench/testbench-coremark.sv @@ -32,7 +32,6 @@ module testbench(); logic [`XLEN-1:0] signature[0:10000]; logic [`XLEN-1:0] testadr; string InstrFName, InstrDName, InstrEName, InstrMName, InstrWName; - logic [31:0] InstrW; logic [`XLEN-1:0] meminit; string tests[]; logic [`AHBW-1:0] HRDATAEXT; @@ -62,9 +61,10 @@ module testbench(); wallypipelinedsoc dut(.*); // Track names of instructions instrTrackerTB it(clk, reset, dut.hart.ieu.dp.FlushE, + dut.hart.ifu.InstrF, dut.hart.ifu.InstrD, dut.hart.ifu.InstrE, - dut.hart.ifu.InstrM, InstrW, - InstrDName, InstrEName, InstrMName, InstrWName); + dut.hart.ifu.InstrM, dut.hart.ifu.InstrW, + InstrFName, InstrDName, InstrEName, InstrMName, InstrWName); // initialize tests initial begin @@ -86,13 +86,13 @@ endmodule /* verilator lint_on WIDTH */ module instrTrackerTB( input logic clk, reset, FlushE, - input logic [31:0] InstrD, + input logic [31:0] InstrF, InstrD, input logic [31:0] InstrE, InstrM, - output logic [31:0] InstrW, - output string InstrDName, InstrEName, InstrMName, InstrWName); + input logic [31:0] InstrW, + output string InstrFName, InstrDName, InstrEName, InstrMName, InstrWName); // stage Instr to Writeback for visualization - flopr #(32) InstrWReg(clk, reset, InstrM, InstrW); + instrNameDecTB fdec(InstrF, InstrFName); instrNameDecTB ddec(InstrD, InstrDName); instrNameDecTB edec(InstrE, InstrEName); instrNameDecTB mdec(InstrM, InstrMName); diff --git a/wally-pipelined/testbench/testbench-imperas.sv b/wally-pipelined/testbench/testbench-imperas.sv index e121c460..49a5263d 100644 --- a/wally-pipelined/testbench/testbench-imperas.sv +++ b/wally-pipelined/testbench/testbench-imperas.sv @@ -90,7 +90,6 @@ string tests64iNOc[] = { "rv64i/I-MISALIGN_JMP-01","2000" }; string tests64i[] = '{ - "rv64i/I-MISALIGN_LDST-01", "2010", "rv64i/I-ADD-01", "3000", "rv64i/I-ADDI-01", "3000", "rv64i/I-ADDIW-01", "3000", @@ -163,6 +162,7 @@ string tests64iNOc[] = { "rv64i/WALLY-SRAI", "3000", "rv64i/WALLY-LOAD", "11bf0", "rv64i/WALLY-JAL", "4000", + "rv64i/WALLY-JALR", "3000", "rv64i/WALLY-STORE", "3000", "rv64i/WALLY-ADDIW", "3000", "rv64i/WALLY-SLLIW", "3000", @@ -289,6 +289,7 @@ string tests32i[] = { "rv32i/WALLY-SUB", "3000", "rv32i/WALLY-STORE", "2000", "rv32i/WALLY-JAL", "3000", + "rv32i/WALLY-JALR", "2000", "rv32i/WALLY-BEQ" ,"4000", "rv32i/WALLY-BNE", "4000 ", "rv32i/WALLY-BLTU", "4000 ", diff --git a/wally-pipelined/testgen/testgen-JAL.py b/wally-pipelined/testgen/testgen-JAL-JALR.py similarity index 53% rename from wally-pipelined/testgen/testgen-JAL.py rename to wally-pipelined/testgen/testgen-JAL-JALR.py index 09f58a56..87c0c423 100755 --- a/wally-pipelined/testgen/testgen-JAL.py +++ b/wally-pipelined/testgen/testgen-JAL-JALR.py @@ -19,9 +19,8 @@ from random import getrandbits from copy import deepcopy ################################## -# functions +# helper functions ################################## - def InitTestGroup(): global TestGroup,TestGroupSizes,AllRegs,UnusedRegs,StoreAdrReg TestGroup += 1 @@ -44,19 +43,21 @@ def registerSelect(): if len(UnusedRegs)==0: InitTestGroup() rd = choice(UnusedRegs) - rs = choice(UnusedRegs) UnusedRegs.remove(rd) + OtherRegs = deepcopy(UnusedRegs) + if 0 in OtherRegs: + OtherRegs.remove(0) + if len(OtherRegs) == 0: + OtherRegs = deepcopy(AllRegs) + OtherRegs.remove(0) + rs = choice(OtherRegs) OtherRegs = deepcopy(AllRegs) OtherRegs.remove(StoreAdrReg) OtherRegs.remove(rd) - try: + if 0 in OtherRegs: OtherRegs.remove(0) - except: - pass - try: + if rs in OtherRegs: OtherRegs.remove(rs) - except: - pass DataReg = choice(OtherRegs) OtherRegs.remove(DataReg) OtherRd = choice(OtherRegs) @@ -65,52 +66,74 @@ def registerSelect(): def addInst(line): global CurrAdr f.write(line) - if ("li x" in line): + if ("li x" in line) and ("slli x" not in line): CurrAdr += 8 if (xlen == 32) else 20 elif ("la x" in line): CurrAdr += 8 else: CurrAdr += 4 -def writeForwardsJumpVector(spacers): - global TestNum - rd, rs, DataReg, OtherRd = registerSelect() - if (xlen==64): - expected = int("fedbca9876540000",16) - unexpected = int("ffff0000ffff0000",16) +def expectValue(expectReg, expectVal, sigOffset): + global TestGroupSizes + TestGroupSizes[TestGroup-1] += 1 + addInst(" "+storecmd+" x"+str(expectReg)+", "+str(wordsize*sigOffset)+"(x"+str(StoreAdrReg)+")\n") + f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(expectReg)+", "+formatstr.format(expectVal)+")\n") + if (xlen == 32): + r.write(formatrefstr.format(expectVal)+"\n") else: - expected = int("fedbca98",16) - unexpected = int("ff00ff00",16) + r.write(formatrefstr.format(expectVal % 2**32)+"\n" + formatrefstr.format(expectVal >> 32)+"\n") +def addJalr(rs,rd,dist): + target = CurrAdr + 20 + dist + target31_12 = CurrAdr >> 12 # 20 bits for lui + target11_0 = target - (target31_12 << 12) # 12 remaining bits + target31_16 = target31_12 >> 4 # lui sign extends, so shift in a leading 0 + target15_12 = target31_12 - (target31_16 << 4) # the nibble we just lost + if target11_0 > 0: + offset = randint(-(1<<11)-1,(1<<11)-2-target11_0) + else: + offset = randint(-(1<<11)-1-target11_0,(1<<11)-2) + addInst(" lui x"+str(rs)+", 0x"+imm20formatstr.format(target31_16)+"\n") + addInst(" addi x"+str(rs)+", x"+str(rs)+", SEXT_IMM(0x0"+imm12formatstr.format(target15_12 << 8)+")\n") + addInst(" slli x"+str(rs)+", x"+str(rs)+", SEXT_IMM(4)\n") + addInst(" addi x"+str(rs)+", x"+str(rs)+", SEXT_IMM(0x"+imm12formatstr.format(0xfff&(offset+target11_0+randint(0,1)))+")\n") + addInst(" JALR x"+str(rd)+", x"+str(rs)+", SEXT_IMM(0x"+imm12formatstr.format(0xfff&(-offset))+")\n") +################################## +# test functions +################################## +def writeForwardsJumpVector(spacers,instr): + global TestNum + TestNum += 1 + rd, rs, DataReg, OtherRd = registerSelect() + # Header f.write("\n") - f.write(" # Testcase "+str(TestNum)+" address cmp result rd:x"+str(rd)+"("+formatstr.format(CurrAdr+44)+") data result rd:x"+str(DataReg)+"("+formatstr.format(expected)+")\n") + f.write(" # Testcase "+str(TestNum)+"\n") + # Test Code addInst(" li x"+str(DataReg)+", "+formatstr.format(expected)+"\n") - addInst(" JAL x"+str(rd)+", 1f\n") + if (instr=="JAL"): + addInst(" JAL x"+str(rd)+", 1f\n") + elif (instr=="JALR"): + dist = spacers*(8 if (xlen == 32) else 20) # Compute distance from linked adr to target adr + addJalr(rs,rd,dist); + else: + exit("invalid instruction") LinkAdr = CurrAdr if (rd!=0) else 0 # rd's expected value for i in range(spacers): addInst(" li x"+str(DataReg)+", "+formatstr.format(unexpected)+"\n") f.write("1:\n") - addInst(" "+storecmd+" x"+str(rd)+", "+str(wordsize*(2*TestNum+0))+"(x"+str(StoreAdrReg)+")\n") - f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(rd)+", "+formatstr.format(LinkAdr)+")\n") - addInst(" "+storecmd+" x"+str(DataReg)+", "+str(wordsize*(2*TestNum+1))+"(x"+str(StoreAdrReg)+")\n") - f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(DataReg)+", "+formatstr.format(expected)+")\n") - writeExpectedToRef(LinkAdr) - writeExpectedToRef(expected) - TestNum = TestNum+1 + # Store values to be verified + expectValue(rd, LinkAdr, 2*TestNum+0) + expectValue(DataReg, expected, 2*TestNum+1) -def writeBackwardsJumpVector(spacers): +def writeBackwardsJumpVector(spacers,instr): global TestNum - rd, rs, DataReg,OtherRd = registerSelect() - if (xlen==64): - expected = int("fedbca9876540000",16) - unexpected = int("ffff0000ffff0000",16) - else: - expected = int("fedbca98",16) - unexpected = int("ff00ff00",16) - + TestNum += 1 + rd, rs, DataReg, OtherRd = registerSelect() + # Header f.write("\n") - f.write(" # Testcase "+str(TestNum)+" address cmp result rd:x"+str(rd)+"("+formatstr.format(CurrAdr+20+8*spacers)+") data result rd:x"+str(DataReg)+"("+formatstr.format(expected)+")\n") + f.write(" # Testcase "+str(TestNum)+"\n") + # Test Code addInst(" JAL x"+str(OtherRd)+", 2f\n") f.write("1:\n") addInst(" li x"+str(DataReg)+", "+formatstr.format(expected)+"\n") @@ -118,29 +141,27 @@ def writeBackwardsJumpVector(spacers): f.write("2:\n") for i in range(spacers): addInst(" li x"+str(DataReg)+", "+formatstr.format(unexpected)+"\n") - addInst(" JAL x"+str(rd)+", 1b\n") + if (instr=="JAL"): + addInst(" JAL x"+str(rd)+", 1b\n") + elif (instr=="JALR"): + dist = -20 - 4 - (1+spacers)*(8 if (xlen == 32) else 20) # Compute distance from linked adr to target adr + addJalr(rs,rd,dist); + else: + exit("invalid instruction") LinkAdr = CurrAdr if (rd!=0) else 0 # rd's expected value f.write("3:\n") - addInst(" "+storecmd+" x"+str(rd)+", "+str(wordsize*(2*TestNum+0))+"(x"+str(StoreAdrReg)+")\n") - f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(rd)+", "+formatstr.format(LinkAdr)+")\n") - addInst(" "+storecmd+" x"+str(DataReg)+", "+str(wordsize*(2*TestNum+1))+"(x"+str(StoreAdrReg)+")\n") - f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(DataReg)+", "+formatstr.format(expected)+")\n") - writeExpectedToRef(LinkAdr) - writeExpectedToRef(expected) - TestNum = TestNum+1 + # Store values to be verified + expectValue(rd, LinkAdr, 2*TestNum+0) + expectValue(DataReg, expected, 2*TestNum+1) def writeChainVector(repetitions,spacers): global TestNum + TestNum += 1 rd, rs, DataReg,OtherRd = registerSelect() - if (xlen==64): - expected = int("fedbca9876540000",16) - unexpected = int("ffff0000ffff0000",16) - else: - expected = int("fedbca98",16) - unexpected = int("ff00ff00",16) - + # Header f.write("\n") - f.write(" # Testcase "+str(TestNum)+" address cmp result rd:x"+str(rd)+"(ugh; if you really wanted to, you could figure it out) data result rd:x"+str(DataReg)+"("+formatstr.format(expected)+")\n") + f.write(" # Testcase "+str(TestNum)+"\n") + # Test Code addInst(" li x"+str(DataReg)+", "+formatstr.format(expected)+"\n") for i in range(repetitions): addInst(" JAL x"+str(OtherRd)+", "+str(3*i+2)+"f\n") @@ -159,57 +180,56 @@ def writeChainVector(repetitions,spacers): for j in range(i): addInst(" li x"+str(DataReg)+", "+formatstr.format(unexpected)+"\n") f.write(str(3*i+3)+":\n") - addInst(" "+storecmd+" x"+str(rd)+", "+str(wordsize*(2*TestNum+0))+"(x"+str(StoreAdrReg)+")\n") - f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(rd)+", "+formatstr.format(LinkAdr)+")\n") - addInst(" "+storecmd+" x"+str(DataReg)+", "+str(wordsize*(2*TestNum+1))+"(x"+str(StoreAdrReg)+")\n") - f.write(" RVTEST_IO_ASSERT_GPR_EQ(x"+str(StoreAdrReg+1)+", x"+str(DataReg)+", "+formatstr.format(expected)+")\n") - writeExpectedToRef(LinkAdr) - writeExpectedToRef(expected) - TestNum = TestNum+1 - - -def writeExpectedToRef(expected): - global TestGroupSizes - TestGroupSizes[TestGroup-1] += 1 - if (xlen == 32): - r.write(formatrefstr.format(expected)+"\n") - else: - r.write(formatrefstr.format(expected % 2**32)+"\n" + formatrefstr.format(expected >> 32)+"\n") + # Store values to be verified + expectValue(rd, LinkAdr, 2*TestNum+0) + expectValue(DataReg, expected, 2*TestNum+1) ################################## # main body ################################## # change these to suite your tests -tests = ["JAL"] +test = 0 +tests = ["JAL","JALR"] author = "Ben Bracker (bbracker@hmc.edu)" xlens = [32,64] -numtests = 100; +numtests = 100 # setup seed(0) # make tests reproducible # generate files for each test -for xlen in xlens: - CurrAdr = int("80000108",16) - TestNum = 0 - TestGroup = 1 - TestGroupSizes = [0] - AllRegs = list(range(0,32)) - UnusedRegs = deepcopy(AllRegs) - StoreAdrReg = 6 # matches what's in header script - UnusedRegs.remove(6) +for test in tests: + for xlen in xlens: + print(test+" "+str(xlen)) + CurrAdr = int("80000108",16) + TestNum = -1 + TestGroup = 1 + TestGroupSizes = [0] + AllRegs = list(range(0,32)) + UnusedRegs = deepcopy(AllRegs) + StoreAdrReg = 6 # matches what's in header script + UnusedRegs.remove(6) + if (xlen==64): + expected = int("fedbca9876540000",16) + unexpected = int("ffff0000ffff0000",16) + else: + expected = int("fedbca98",16) + unexpected = int("ff00ff00",16) + + formatstrlen = str(int(xlen/4)) + formatstr = "0x{:0" + formatstrlen + "x}" # format as xlen-bit hexadecimal number + formatrefstr = "{:08x}" # format as xlen-bit hexadecimal number with no leading 0x + imm20formatstr = "{:05x}" + imm12formatstr = "{:03x}" + + if (xlen == 32): + storecmd = "sw" + wordsize = 4 + else: + storecmd = "sd" + wordsize = 8 - formatstrlen = str(int(xlen/4)) - formatstr = "0x{:0" + formatstrlen + "x}" # format as xlen-bit hexadecimal number - formatrefstr = "{:08x}" # format as xlen-bit hexadecimal number with no leading 0x - if (xlen == 32): - storecmd = "sw" - wordsize = 4 - else: - storecmd = "sd" - wordsize = 8 - for test in tests: imperaspath = "../../imperas-riscv-tests/riscv-test-suite/rv" + str(xlen) + "i/" basename = "WALLY-" + test fname = imperaspath + "src/" + basename + ".S" @@ -221,7 +241,7 @@ for xlen in xlens: f.write("///////////////////////////////////////////\n") f.write("// "+fname+ "\n") f.write("//\n") - f.write("// This file can be used to test the RISC-V JAL instruction.\n") + f.write("// This file can be used to test the RISC-V JAL(R) instruction.\n") f.write("// But be warned that altering the test environment may break this test!\n") f.write("// In order to work, this test expects that the first instruction (la)\n") f.write("// be allocated at 0x80000100.\n") @@ -235,14 +255,24 @@ for xlen in xlens: f.write(line) # print directed test vectors - for i in range(0,31): - writeForwardsJumpVector(randint(0,4)) - for i in range(0,31): - writeBackwardsJumpVector(randint(0,4)) - writeForwardsJumpVector(100) - writeBackwardsJumpVector(100) - writeChainVector(6,True) - writeChainVector(16,False) + if test == "JAL": + for i in range(0,31): + writeForwardsJumpVector(randint(0,4),"JAL") + for i in range(0,31): + writeBackwardsJumpVector(randint(0,4),"JAL") + writeForwardsJumpVector(100,"JAL") + writeBackwardsJumpVector(100,"JAL") + writeChainVector(6,True) + writeChainVector(16,False) + elif test == "JALR": + for i in range(0,31): + writeForwardsJumpVector(randint(0,4),"JALR") + for i in range(0,31): + writeBackwardsJumpVector(randint(0,4),"JALR") + # can't make these latter two too long else 12 bit immediate overflows + # (would need to lui or slli rs to achieve longer ranges) + writeForwardsJumpVector(15,"JALR") + writeBackwardsJumpVector(15,"JALR") # print footer h = open("testgen_footer.S", "r") @@ -254,7 +284,3 @@ for xlen in xlens: f.write("\nRV_COMPLIANCE_DATA_END\n") f.close() r.close() - - - - diff --git a/wally-pipelined/testgen/testgen-VIRTUALMEMORY.py b/wally-pipelined/testgen/testgen-VIRTUALMEMORY.py new file mode 100644 index 00000000..07bb6e37 --- /dev/null +++ b/wally-pipelined/testgen/testgen-VIRTUALMEMORY.py @@ -0,0 +1,285 @@ +#!/usr/bin/python3 +################################## +# testgen-VIRTUALMEMORY.py +# +# Jessica Torrey 01 March 2021 +# Thomas Fleming 01 March 2021 +# +# Generate an ad-hoc test program for RISC-V Design Validation. Tests the +# functionality of the memory management unit (MMU). +################################## + +################################## +# libraries +################################## +from datetime import datetime +from random import randint, seed, getrandbits +from textwrap import dedent +from virtual_memory_util import * + +################################## +# global structures +################################## + +testcase_num = 0 +signature_len = 2000 +signature = [0xff for _ in range(signature_len)] + +################################## +# functions +################################## + +def rand_reg(): + """ + Produce a random register from 1 to 31 (skipping 6, since r6 is used for + other things). + """ + r = randint(1,30) + if r >= 6: + r += 1 + return r + +def rand_regs(): + """ + Generate two random, unequal register numbers (skipping x6). + """ + rs1 = rand_reg() + rs2 = rand_reg() + while rs1 == rs2: + rs2 = rand_reg() + + return rs1, rs2 + +def initialize_page_directory() + +def generate_case(xlen, instruction, value_register, value, addr_register, offset, base_delta): + """ + Create assembly code for a given STORE test case, returned as a string. + + Generates an `xlen`-bit test case for `instruction` (one of sb, sh, sw, or + sd) that loads `value` into `value_register`, then attempts to store that + value in memory at address (x6 + `base_delta`). The test case + assumes the current base address for the signature is in register x6. + + The form of the STORE instruction is as follows: + + `instruction` `value_register` `offset`(`addr_register`) + """ + global testcase_num + + hex_value = f"{value:0{xlen // 4}x}" + + data = f"""# Testcase {testcase_num}: source: x{value_register} (value 0x{hex_value}), destination: {offset}(x{addr_register}) ({base_delta} bytes into signature) + addi x{addr_register}, x6, {base_delta} + li x{value_register}, MASK_XLEN({-offset}) + add x{addr_register}, x{addr_register}, x{value_register} + li x{value_register}, 0x{hex_value} + {instruction} x{value_register}, {offset}(x{addr_register}) + """ + + #update_signature(store_to_size[instruction], value, base_delta) + + testcase_num += 1 + return data + +def validate_memory(scratch_register, value_register, value, base_delta, length): + """ + Create assembly code to verify that `length` bytes at mem[x6 + `base_delta`] + store `value`. + + Assumes x6 stores the current base address for the signature. + """ + truncated_value = value & (2**(length * 8) - 1) + hex_value = f"{truncated_value:0{length * 2}x}" + + load = size_to_load[length] + data = f"""addi x{scratch_register}, x6, {base_delta} + {load} x{value_register}, 0(x{scratch_register}) + RVTEST_IO_ASSERT_GPR_EQ(x{scratch_register}, x{value_register}, 0x{hex_value}) + + """ + return data + +def update_signature(length, value, base_delta): + """ + Write the lower `length` bytes of `value` to the little-endian signature + array, starting at byte `base_delta`. + """ + truncated_value = value & (2**(length * 8) - 1) + value_bytes = truncated_value.to_bytes(length, 'little') + #print("n: {}, value: {:x}, trunc: {:x}, length: {}, bd: {:x}".format(testcase_num, value, truncated_value, length, base_delta)) + for i in range(length): + signature[base_delta + i] = value_bytes[i] + +def write_signature(outfile): + """ + Writes successive 32-bit words from the signature array into a given file. + """ + for i in range(0, signature_len, 4): + word = list(reversed(signature[i:i+4])) + hexword = bytearray(word).hex() + outfile.write(f"{hexword}\n") + +def write_header(outfile): + """ + Write the name of the test file, authors, and creation date. + """ + outfile.write(dedent(f"""\ + /////////////////////////////////////////// + // + // WALLY-STORE + // + // Author: {author} + // + // Created {str(datetime.now())} + """)) + outfile.write(open("testgen_header.S", "r").read()) + +def write_footer(outfile): + """ + Write necessary closing code, including a data section for the signature. + """ + outfile.write(open("testgen_footer.S", 'r').read()) + data_section = dedent(f"""\ + \t.fill {signature_len}, 1, -1 + RV_COMPLIANCE_DATA_END + """) + outfile.write(data_section) + +################################## +# test cases +################################## + +def write_basic_tests(outfile, xlen, instr_len, num, base_delta): + """ + Test basic functionality of STORE, using random registers, offsets, and + values. + + Creates `num` tests for a single store instruction of length `instr_len`, + writing to memory at consecutive locations, starting `base_delta` bytes from + the start of the signature. + + Returns the number of bytes from the start of the signature where testing + ended. + """ + instruction = size_to_store[instr_len] + for i in range(num): + value_register, addr_register = rand_regs() + offset = randint(-2**(12 - 1), 2**(12 - 1) - 1) + value = randint(0, 2**(instr_len * 8) - 1) + test = generate_case(xlen, instruction, value_register, value, addr_register, offset, base_delta) + validate = validate_memory(addr_register, value_register, value, base_delta, instr_len) + outfile.write(test) + outfile.write(validate) + base_delta += instr_len + return base_delta + +def write_random_store_tests(outfile, xlen, instr_len, num, min_base_delta): + """ + Test random access of STORE, using random registers, offsets, values, and + memory locations. + + Creates `num` tests for a single store instruction of length `instr_len`, + writing to memory at random locations between `min_base_delta` bytes past + the start of the signature to the end of the signature. + """ + instruction = size_to_store[instr_len] + for i in range(num): + base_delta = randint(min_base_delta, signature_len - 1) + base_delta -= (base_delta % instr_len) + value_register, addr_register = rand_regs() + offset = randint(-2**(12 - 1), 2**(12 - 1) - 1) + value = randint(0, 2**(instr_len * 8) - 1) + + test = generate_case(xlen, instruction, value_register, value, addr_register, offset, base_delta) + validate = validate_memory(addr_register, value_register, value, base_delta, instr_len) + outfile.write(test) + outfile.write(validate) + +def write_repeated_store_tests(outfile, xlen, instr_len, num, base_delta): + """ + Test repeated access of STORE, using random registers, offsets, values, and a + single memory location. + + Creates `num` tests for a single store instruction of length `instr_len`, + writing to memory `base_delta` bytes past the start of the signature. + """ + instruction = size_to_store[instr_len] + for i in range(num): + value_register, addr_register = rand_regs() + offset = 0 + value = (1 << ((2 * i) % xlen)) + + test = generate_case(xlen, instruction, value_register, value, addr_register, offset, base_delta) + validate = validate_memory(addr_register, value_register, value, base_delta, instr_len) + + outfile.write(test) + outfile.write(validate) + +def write_corner_case_tests(outfile, xlen, instr_len, base_delta): + instruction = size_to_store[instr_len] + + corner_cases_32 = [0x0, 0x10000001, 0x01111111] + corner_cases_64 = [0x1000000000000001, 0x0111111111111111] + corner_cases = corner_cases_32 + if xlen == 64: + corner_cases += corner_cases_64 + + for offset in corner_cases: + for value in corner_cases: + value_register, addr_register = rand_regs() + test = generate_case(xlen, instruction, value_register, value, addr_register, offset, base_delta) + validate = validate_memory(addr_register, value_register, value, base_delta, instr_len) + + outfile.write(test) + outfile.write(validate) + + base_delta += instr_len + + return base_delta + +################################## +# main body +################################## + +if __name__ == "__main__": + instructions = ["sd", "sw", "sh", "sb"] + author = "Jessica Torrey & Thomas Fleming " + xlens = [32, 64] + numrand = 100 + + # setup + seed(0) # make tests reproducible + + for xlen in xlens: + if (xlen == 32): + wordsize = 4 + else: + wordsize = 8 + + fname = f"../../imperas-riscv-tests/riscv-test-suite/rv{xlen}i/src/WALLY-VIRTUALMEMORY.S" + refname = f"../../imperas-riscv-tests/riscv-test-suite/rv{xlen}i/references/WALLY-VIRTUALMEMORY.reference_output" + f = open(fname, "w") + r = open(refname, "w") + + write_header(f) + + base_delta = 0 + + for instruction in instructions: + if xlen == 32 and instruction == 'sd': + continue + instr_len = store_to_size[instruction] + base_delta = write_basic_tests(f, xlen, instr_len, 5, base_delta) + write_repeated_store_tests(f, xlen, instr_len, 32, base_delta) + write_random_store_tests(f, xlen, instr_len, 5, base_delta + wordsize) + + write_footer(f) + + write_signature(r) + f.close() + r.close() + + # Reset testcase_num and signature + testcase_num = 0 + signature = [0xff for _ in range(signature_len)] \ No newline at end of file diff --git a/wally-pipelined/testgen/virtual_memory_util.py b/wally-pipelined/testgen/virtual_memory_util.py new file mode 100644 index 00000000..83b34d5b --- /dev/null +++ b/wally-pipelined/testgen/virtual_memory_util.py @@ -0,0 +1,125 @@ +#!/usr/bin/python3 +################################## +# virtual_memory_util.py +# +# Jessica Torrey 01 March 2021 +# Thomas Fleming 01 March 2021 +# +# Utility functions for simulating and testing virtual memory on RISC-V. +################################## + +################################## +# libraries +################################## +from datetime import datetime +from random import randint, seed, getrandbits +from textwrap import dedent + +################################## +# global structures +################################## +PTE_D = 1 << 7 +PTE_A = 1 << 6 +PTE_G = 1 << 5 +PTE_U = 1 << 4 +PTE_X = 1 << 3 +PTE_W = 1 << 2 +PTE_R = 1 << 1 +PTE_V = 1 << 0 + + +pgdir = [] + +testcase_num = 0 +signature_len = 2000 +signature = [0xff for _ in range(signature_len)] + +################################## +# classes +################################## +class Architecture: + def __init__(self, xlen): + if (xlen == 32): + self.PTESIZE = 4 + + self.VPN_BITS = 20 + self.VPN_SEGMENT_BITS = 10 + + self.PPN_BITS = 22 + + self.LEVELS = 2 + elif (xlen == 64): + self.PTESIZE = 8 + + self.VPN_BITS = 27 + self.VPN_SEGMENT_BITS = 9 + + self.PPN_BITS = 44 + + self.LEVELS = 3 + else: + raise ValueError('Only rv32 and rv64 are allowed.') + + self.PGSIZE = 2**12 + self.NPTENTRIES = self.PGSIZE // self.PTESIZE + self.PTE_BITS = 8 * self.PTESIZE + self.OFFSET_BITS = 12 + self.FLAG_BITS = 8 + +class PageTableEntry: + def __init__(self, ppn, flags, arch): + assert 0 <= ppn and ppn < 2**arch.PPN_BITS, "Invalid physical page number for PTE" + assert 0 <= flags and flags < 2**arch.FLAG_BITS, "Invalid flags for PTE" + self.ppn = ppn + self.flags = flags + self.arch = arch + + def entry(self): + return (self.ppn << (self.arch.PTE_BITS - self.arch.PPN_BITS)) | self.flags + + def __str__(self): + return "0x{0:0{1}x}".format(self.entry(), self.arch.PTESIZE*2) + + def __repr__(self): + return f"" + +class PageTable: + """ + Represents a single level of the page table, with + """ + def __init__(self, name, arch): + self.table = {} + self.name = name + self.arch = arch + + def add_entry(self, vpn_segment, ppn_segment, flags, linked_table = None): + if not (0 <= vpn_segment < 2**self.arch.VPN_SEGMENT_BITS): + raise ValueError("Invalid virtual page segment number") + self.table[vpn_segment] = (PageTableEntry(ppn_segment, flags, self.arch), linked_table) + + def add_mapping(self, va, pa, flags): + if not (0 <= va < 2**self.arch.VPN_BITS): + raise ValueError("Invalid virtual page number") + for level in range(self.arch.LEVELS - 1, -1, -1): + + + + + def assembly(self): + entries = list(sorted(self.table.items(), key=lambda item: item[0])) + current_index = 0 + asm = f".balign {self.arch.PGSIZE}\n{self.name}:\n" + for entry in entries: + vpn_index, (pte, _) = entry + if current_index < vpn_index: + asm += f" .fill {vpn_index - current_index}, {self.arch.PTESIZE}, 0\n" + asm += f" .4byte {str(pte)}\n" + current_index = vpn_index + 1 + if current_index < self.arch.NPTENTRIES: + asm += f" .fill {self.arch.NPTENTRIES - current_index}, {self.arch.PTESIZE}, 0\n" + return asm + +################################## +# functions +################################## +