/////////////////////////////////////////// // testbench-linux.sv // // Written: nboorstin@g.hmc.edu 2021 // Modified: // // Purpose: Testbench for buildroot or busybear linux // // A component of the Wally configurable RISC-V project. // // Copyright (C) 2021 Harvey Mudd College & Oklahoma State University // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, // modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software // is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT // OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /////////////////////////////////////////// `include "wally-config.vh" module testbench(); logic clk, reset; logic [31:0] GPIOPinsIn; logic [31:0] GPIOPinsOut, GPIOPinsEn; // instantiate device to be tested logic [31:0] CheckInstrD; logic [`AHBW-1:0] HRDATA; logic [31:0] HADDR; logic [`AHBW-1:0] HWDATA; logic HWRITE; logic [2:0] HSIZE; logic [2:0] HBURST; logic [3:0] HPROT; logic [1:0] HTRANS; logic HMASTLOCK; logic HCLK, HRESETn; logic [`AHBW-1:0] HRDATAEXT; logic HREADYEXT, HRESPEXT; logic UARTSout; assign GPIOPinsIn = 0; assign UARTSin = 1; // instantiate processor and memories wallypipelinedsoc dut(.*); /** * Walk the page table stored in dtim according to sv39 logic and translate a * virtual address to a physical address. * * See section 4.3.2 of the RISC-V Privileged specification for a full * explanation of the below algorithm. */ function logic [`XLEN-1:0] adrTranslator( input logic [`XLEN-1:0] adrIn); begin logic SvMode, PTE_R, PTE_X; logic [`XLEN-1:0] SATP, PTE; logic [55:0] BaseAdr, PAdr; logic [8:0] VPN [0:2]; logic [11:0] Offset; int i; // Grab the SATP register from privileged unit SATP = dut.hart.priv.csr.SATP_REGW; // Split the virtual address into page number segments and offset VPN[2] = adrIn[38:30]; VPN[1] = adrIn[29:21]; VPN[0] = adrIn[20:12]; Offset = adrIn[11:0]; // We do not support sv48; only sv39 SvMode = SATP[63]; // Only perform translation if translation is on and the processor is not // in machine mode if (SvMode && (dut.hart.priv.PrivilegeModeW != `M_MODE)) begin BaseAdr = SATP[43:0] << 12; for (i = 2; i >= 0; i--) begin PAdr = BaseAdr + (VPN[i] << 3); // dtim.RAM is 64-bit addressed. PAdr specifies a byte. We right shift // by 3 (the PTE size) to get the requested 64-bit PTE. PTE = dut.uncore.dtim.RAM[PAdr >> 3]; PTE_R = PTE[1]; PTE_X = PTE[3]; if (PTE_R || PTE_X) begin // Leaf page found break; end else begin // Go to next level of table BaseAdr = PTE[53:10] << 12; end end // Determine which parts of the PTE page number to use based on the // level of the page table we reached. if (i == 2) begin // Gigapage assign adrTranslator = {8'b0, PTE[53:28], VPN[1], VPN[0], Offset}; end else if (i == 1) begin // Megapage assign adrTranslator = {8'b0, PTE[53:19], VPN[0], Offset}; end else begin // Kilopage assign adrTranslator = {8'b0, PTE[53:10], Offset}; end end else begin // Direct translation if address translation is not on assign adrTranslator = adrIn; end end endfunction // initialize test initial begin reset <= 1; # 22; reset <= 0; end // read pc trace file integer data_file_PC, scan_file_PC; initial begin data_file_PC = $fopen({`LINUX_TEST_VECTORS,"parsedPC.txt"}, "r"); if (data_file_PC == 0) begin $display("file couldn't be opened"); $stop; end end integer data_file_PCW, scan_file_PCW; initial begin data_file_PCW = $fopen({`LINUX_TEST_VECTORS,"parsedPC.txt"}, "r"); if (data_file_PCW == 0) begin $display("file couldn't be opened"); $stop; end end // read register trace file integer data_file_rf, scan_file_rf; initial begin data_file_rf = $fopen({`LINUX_TEST_VECTORS,"parsedRegs.txt"}, "r"); if (data_file_rf == 0) begin $display("file couldn't be opened"); $stop; end end // read CSR trace file integer data_file_csr, scan_file_csr; initial begin data_file_csr = $fopen({`LINUX_TEST_VECTORS,"parsedCSRs.txt"}, "r"); if (data_file_csr == 0) begin $display("file couldn't be opened"); $stop; end end // read memreads trace file integer data_file_memR, scan_file_memR; initial begin data_file_memR = $fopen({`LINUX_TEST_VECTORS,"parsedMemRead.txt"}, "r"); if (data_file_memR == 0) begin $display("file couldn't be opened"); $stop; end end // read memwrite trace file integer data_file_memW, scan_file_memW; initial begin data_file_memW = $fopen({`LINUX_TEST_VECTORS,"parsedMemWrite.txt"}, "r"); if (data_file_memW == 0) begin $display("file couldn't be opened"); $stop; end end // initial loading of memories initial begin $readmemh({`LINUX_TEST_VECTORS,"bootmem.txt"}, dut.uncore.bootdtim.RAM, 'h1000 >> 3); $readmemh({`LINUX_TEST_VECTORS,"ram.txt"}, dut.uncore.dtim.RAM); $readmemb(`TWO_BIT_PRELOAD, dut.hart.ifu.bpred.bpred.Predictor.DirPredictor.PHT.memory); $readmemb(`BTB_PRELOAD, dut.hart.ifu.bpred.bpred.TargetPredictor.memory.memory); end integer warningCount = 0; integer instrs; //logic[63:0] adrTranslation[4:0]; //string translationType[4:0] = {"rf", "writeAdr", "PCW", "PC", "readAdr"}; //initial begin // for(int i=0; i<5; i++) begin // adrTranslation[i] = 64'b0; // end //end //function logic equal(logic[63:0] adr, logic[63:0] adrExpected, integer func); // if (adr[11:0] !== adrExpected[11:0]) begin // equal = 1'b0; // end else begin // equal = 1'b1; // if ((adr+adrTranslation[func]) !== adrExpected) begin // adrTranslation[func] = adrExpected - adr; // $display("warning: probably new address translation %x for %s at instr %0d", adrTranslation[func], translationType[func], instrs); // warningCount += 1; // end // end //endfunction // pretty sure this isn't necessary anymore, but keeping this for now since its easier function logic equal(logic[63:0] adr, logic[63:0] adrExpected, integer func); equal = adr === adrExpected; endfunction `define ERROR \ #10; \ $display("processed %0d instructions with %0d warnings", instrs, warningCount); \ $stop; logic [63:0] pcExpected; logic [63:0] regExpected; integer regNumExpected; logic [`XLEN-1:0] PCW; flopenr #(`XLEN) PCWReg(clk, reset, ~dut.hart.ieu.dp.StallW, dut.hart.ifu.PCM, PCW); genvar i; generate for(i=1; i<32; i++) begin always @(dut.hart.ieu.dp.regf.rf[i]) begin if ($time == 0) begin scan_file_rf = $fscanf(data_file_rf, "%x\n", regExpected); if (dut.hart.ieu.dp.regf.rf[i] != regExpected) begin $display("%0t ps, instr %0d: rf[%0d] does not equal rf expected: %x, %x", $time, instrs, i, dut.hart.ieu.dp.regf.rf[i], regExpected); `ERROR end end else begin scan_file_rf = $fscanf(data_file_rf, "%d\n", regNumExpected); scan_file_rf = $fscanf(data_file_rf, "%x\n", regExpected); if (i != regNumExpected) begin $display("%0t ps, instr %0d: wrong register changed: %0d, %0d expected to switch to %x from %x", $time, instrs, i, regNumExpected, regExpected, dut.hart.ieu.dp.regf.rf[regNumExpected]); `ERROR end if (~equal(dut.hart.ieu.dp.regf.rf[i],regExpected, 0)) begin $display("%0t ps, instr %0d: rf[%0d] does not equal rf expected: %x, %x", $time, instrs, i, dut.hart.ieu.dp.regf.rf[i], regExpected); `ERROR end //if (dut.hart.ieu.dp.regf.rf[i] !== regExpected) begin // force dut.hart.ieu.dp.regf.rf[i] = regExpected; // release dut.hart.ieu.dp.regf.rf[i]; //end end end end endgenerate // RAM and bootram are addressed in 64-bit blocks - this logic handles R/W // including subwords. Brief explanation on signals: // // readMask: bitmask of bits to read / write, left-shifted to align with // nearest 64-bit boundary - examples // HSIZE = 0 -> readMask = 11111111 // HSIZE = 1 -> readMask = 1111111111111111 // // In the linux boot, the processor spends the first ~5 instructions in // bootram, before jr jumps to main RAM logic [63:0] readMask; assign readMask = ((1 << (8*(1 << HSIZE))) - 1) << 8 * HADDR[2:0]; logic [`XLEN-1:0] readAdrExpected, readAdrTranslated; always @(dut.HRDATA) begin #2; if (dut.hart.MemRWM[1] && (dut.hart.ebu.CaptureDataM) && dut.HRDATA !== {64{1'bx}}) begin //$display("%0t", $time); if($feof(data_file_memR)) begin $display("no more memR data to read"); `ERROR end scan_file_memR = $fscanf(data_file_memR, "%x\n", readAdrExpected); scan_file_memR = $fscanf(data_file_memR, "%x\n", HRDATA); assign readAdrTranslated = adrTranslator(readAdrExpected); if (~equal(HADDR,readAdrTranslated,4)) begin $display("%0t ps, instr %0d: HADDR does not equal readAdrExpected: %x, %x", $time, instrs, HADDR, readAdrTranslated); `ERROR end if ((readMask & HRDATA) !== (readMask & dut.HRDATA)) begin if (HADDR inside `LINUX_FIX_READ) begin //$display("warning %0t ps, instr %0d, adr %0d: forcing HRDATA to expected: %x, %x", $time, instrs, HADDR, HRDATA, dut.HRDATA); force dut.uncore.HRDATA = HRDATA; #9; release dut.uncore.HRDATA; warningCount += 1; end else begin $display("%0t ps, instr %0d: ExpectedHRDATA does not equal dut.HRDATA: %x, %x from address %x, %x", $time, instrs, HRDATA, dut.HRDATA, HADDR, HSIZE); `ERROR end end //end else if(dut.hart.MemRWM[1]) begin // $display("%x, %x, %x, %t", HADDR, dut.PCF, dut.HRDATA, $time); end end logic [`XLEN-1:0] writeDataExpected, writeAdrExpected, writeAdrTranslated; // this might need to change //always @(HWDATA or HADDR or HSIZE or HWRITE) begin always @(negedge HWRITE) begin //#1; if ($time != 0) begin if($feof(data_file_memW)) begin $display("no more memW data to read"); `ERROR end scan_file_memW = $fscanf(data_file_memW, "%x\n", writeDataExpected); scan_file_memW = $fscanf(data_file_memW, "%x\n", writeAdrExpected); assign writeAdrTranslated = adrTranslator(writeAdrExpected); if (writeDataExpected != HWDATA && ~dut.uncore.HSELPLICD) begin $display("%0t ps, instr %0d: HWDATA does not equal writeDataExpected: %x, %x", $time, instrs, HWDATA, writeDataExpected); `ERROR end if (~equal(writeAdrTranslated,HADDR,1) && ~dut.uncore.HSELPLICD) begin $display("%0t ps, instr %0d: HADDR does not equal writeAdrExpected: %x, %x", $time, instrs, HADDR, writeAdrTranslated); `ERROR end end end integer totalCSR = 0; logic [99:0] StartCSRexpected[63:0]; string StartCSRname[99:0]; initial begin while(1) begin scan_file_csr = $fscanf(data_file_csr, "%s\n", StartCSRname[totalCSR]); if(StartCSRname[totalCSR] == "---") begin break; end scan_file_csr = $fscanf(data_file_csr, "%x\n", StartCSRexpected[totalCSR]); totalCSR = totalCSR + 1; end end always @(dut.hart.priv.csr.genblk1.csrm.MCAUSE_REGW) begin if (dut.hart.priv.csr.genblk1.csrm.MCAUSE_REGW == 2 && instrs > 1) begin $display("!!!!!! illegal instruction !!!!!!!!!!"); $display("(as a reminder, MCAUSE and MEPC are set by this)"); $display("at %0t ps, instr %0d, HADDR %x", $time, instrs, HADDR); `ERROR end if (dut.hart.priv.csr.genblk1.csrm.MCAUSE_REGW == 5 && instrs != 0) begin $display("!!!!!! illegal (physical) memory access !!!!!!!!!!"); $display("(as a reminder, MCAUSE and MEPC are set by this)"); $display("at %0t ps, instr %0d, HADDR %x", $time, instrs, HADDR); `ERROR end end `define CHECK_CSR2(CSR, PATH) \ string CSR; \ logic [63:0] expected``CSR``; \ //CSR checking \ always @(``PATH``.``CSR``_REGW) begin \ if ($time > 1) begin \ if ("SEPC" == `"CSR`") begin #1; end \ if ("SCAUSE" == `"CSR`") begin #2; end \ if ("SSTATUS" == `"CSR`") begin #3; end \ scan_file_csr = $fscanf(data_file_csr, "%s\n", CSR); \ scan_file_csr = $fscanf(data_file_csr, "%x\n", expected``CSR``); \ if(CSR.icompare(`"CSR`")) begin \ $display("%0t ps, instr %0d: %s changed, expected %s", $time, instrs, `"CSR`", CSR); \ end \ if(``PATH``.``CSR``_REGW != ``expected``CSR) begin \ $display("%0t ps, instr %0d: %s does not equal %s expected: %x, %x", $time, instrs, `"CSR`", CSR, ``PATH``.``CSR``_REGW, ``expected``CSR); \ `ERROR \ end \ end else begin \ if (!(`BUILDROOT == 1 && "MSTATUS" == `"CSR`")) begin \ for(integer j=0; j