mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-11 06:05:49 +00:00
Merge branch 'main' into busybear
Conflicts: wally-pipelined/src/uncore/imem.sv
This commit is contained in:
commit
fde94f9057
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
234
wally-pipelined/src/mmu/tlb.sv
Normal file
234
wally-pipelined/src/mmu/tlb.sv
Normal file
@ -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
|
@ -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;
|
||||
|
@ -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
|
233
wally-pipelined/src/tlb_toy/tlb_toy.sv.OLD
Normal file
233
wally-pipelined/src/tlb_toy/tlb_toy.sv.OLD
Normal file
@ -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
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 ",
|
||||
|
@ -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()
|
||||
|
||||
|
||||
|
||||
|
285
wally-pipelined/testgen/testgen-VIRTUALMEMORY.py
Normal file
285
wally-pipelined/testgen/testgen-VIRTUALMEMORY.py
Normal file
@ -0,0 +1,285 @@
|
||||
#!/usr/bin/python3
|
||||
##################################
|
||||
# testgen-VIRTUALMEMORY.py
|
||||
#
|
||||
# Jessica Torrey <jtorrey@hmc.edu> 01 March 2021
|
||||
# Thomas Fleming <tfleming@hmc.edu> 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 <jtorrey@hmc.edu> & Thomas Fleming <tfleming@hmc.edu>"
|
||||
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)]
|
125
wally-pipelined/testgen/virtual_memory_util.py
Normal file
125
wally-pipelined/testgen/virtual_memory_util.py
Normal file
@ -0,0 +1,125 @@
|
||||
#!/usr/bin/python3
|
||||
##################################
|
||||
# virtual_memory_util.py
|
||||
#
|
||||
# Jessica Torrey <jtorrey@hmc.edu> 01 March 2021
|
||||
# Thomas Fleming <tfleming@hmc.edu> 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"<ppn: {hex(self.ppn)}, flags: {self.flags:08b}>"
|
||||
|
||||
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
|
||||
##################################
|
||||
|
Loading…
Reference in New Issue
Block a user