Merge branch 'main' into busybear

Conflicts:
	wally-pipelined/src/uncore/imem.sv
This commit is contained in:
Noah Boorstin 2021-03-04 20:16:03 +00:00
commit fde94f9057
19 changed files with 1235 additions and 430 deletions

View File

@ -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

View File

@ -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;

View File

@ -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 */

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View 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

View File

@ -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;

View File

@ -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

View 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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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 ",

View File

@ -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()

View 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)]

View 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
##################################