From f20bda673118e0a2b0fedecae4341638b089d8b0 Mon Sep 17 00:00:00 2001 From: Iztok Jeras Date: Sat, 26 Mar 2022 21:09:10 +0100 Subject: [PATCH 1/7] RTL: cosmetic changes, Verilog-2001 style for parameters and whitespace --- FemtoRV/RTL/PROCESSOR/femtorv32_quark.v | 76 ++++++++++++------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v b/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v index 52705e57..afd5cbcb 100644 --- a/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v +++ b/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v @@ -34,23 +34,23 @@ `define NRV_ABI "ilp32" `define NRV_OPTIMIZE "-Os" -module FemtoRV32( - input clk, +module FemtoRV32 #( + parameter RESET_ADDR = 32'h00000000, + parameter ADDR_WIDTH = 24 +)( + input clk, output [31:0] mem_addr, // address bus output [31:0] mem_wdata, // data to be written - output [3:0] mem_wmask, // write mask for the 4 bytes of each word - input [31:0] mem_rdata, // input lines for both data and instr - output mem_rstrb, // active to initiate memory read (used by IO) - input mem_rbusy, // asserted if memory is busy reading value - input mem_wbusy, // asserted if memory is busy writing value + output [3:0] mem_wmask, // write mask for the 4 bytes of each word + input [31:0] mem_rdata, // input lines for both data and instr + output mem_rstrb, // active to initiate memory read (used by IO) + input mem_rbusy, // asserted if memory is busy reading value + input mem_wbusy, // asserted if memory is busy writing value - input reset // set to 0 to reset the processor + input reset // set to 0 to reset the processor ); - parameter RESET_ADDR = 32'h00000000; - parameter ADDR_WIDTH = 24; - /***************************************************************************/ // Instruction decoding. /***************************************************************************/ @@ -100,6 +100,7 @@ module FemtoRV32( (* no_rw_check *) reg [31:0] registerFile [31:0]; + // write access always @(posedge clk) begin if (writeBack) if (rdId != 0) @@ -154,16 +155,16 @@ module FemtoRV32( always @(posedge clk) begin if(aluWr) begin if (funct3IsShift) begin // SLL, SRA, SRL - aluReg <= aluIn1; - aluShamt <= aluIn2[4:0]; - end + aluReg <= aluIn1; + aluShamt <= aluIn2[4:0]; + end end `ifdef NRV_TWOLEVEL_SHIFTER else if(|aluShamt[4:2]) begin // Shift by 4 aluShamt <= aluShamt - 4; - aluReg <= funct3Is[1] ? aluReg << 4 : - {{4{instr[30] & aluReg[31]}}, aluReg[31:4]}; + aluReg <= funct3Is[1] ? aluReg << 4 : + {{4{instr[30] & aluReg[31]}}, aluReg[31:4]}; end else `endif // Compact form of: @@ -173,8 +174,8 @@ module FemtoRV32( if (|aluShamt) begin aluShamt <= aluShamt - 1; - aluReg <= funct3Is[1] ? aluReg << 1 : // SLL - {instr[30] & aluReg[31], aluReg[31:1]}; // SRA,SRL + aluReg <= funct3Is[1] ? aluReg << 1 : // SLL + {instr[30] & aluReg[31], aluReg[31:1]}; // SRA,SRL end end @@ -204,20 +205,20 @@ module FemtoRV32( // branch->PC+Bimm AUIPC->PC+Uimm JAL->PC+Jimm // Equivalent to PCplusImm = PC + (isJAL ? Jimm : isAUIPC ? Uimm : Bimm) wire [ADDR_WIDTH-1:0] PCplusImm = PC + ( instr[3] ? Jimm[ADDR_WIDTH-1:0] : - instr[4] ? Uimm[ADDR_WIDTH-1:0] : - Bimm[ADDR_WIDTH-1:0] ); + instr[4] ? Uimm[ADDR_WIDTH-1:0] : + Bimm[ADDR_WIDTH-1:0] ); // A separate adder to compute the destination of load/store. // testing instr[5] is equivalent to testing isStore in this context. wire [ADDR_WIDTH-1:0] loadstore_addr = rs1[ADDR_WIDTH-1:0] + - (instr[5] ? Simm[ADDR_WIDTH-1:0] : Iimm[ADDR_WIDTH-1:0]); + (instr[5] ? Simm[ADDR_WIDTH-1:0] : Iimm[ADDR_WIDTH-1:0]); /* verilator lint_off WIDTH */ // internal address registers and cycles counter may have less than // 32 bits, so we deactivate width test for mem_addr and writeBackData assign mem_addr = state[WAIT_INSTR_bit] | state[FETCH_INSTR_bit] ? - PC : loadstore_addr ; + PC : loadstore_addr ; /***************************************************************************/ // The value written back to the register file. @@ -251,7 +252,7 @@ module FemtoRV32( // - funct3[2] (instr[14]): 0->do sign expansion 1->no sign expansion wire LOAD_sign = - !instr[14] & (mem_byteAccess ? LOAD_byte[7] : LOAD_halfword[15]); + !instr[14] & (mem_byteAccess ? LOAD_byte[7] : LOAD_halfword[15]); wire [31:0] LOAD_data = mem_byteAccess ? {{24{LOAD_sign}}, LOAD_byte} : @@ -259,10 +260,10 @@ module FemtoRV32( mem_rdata ; wire [15:0] LOAD_halfword = - loadstore_addr[1] ? mem_rdata[31:16] : mem_rdata[15:0]; + loadstore_addr[1] ? mem_rdata[31:16] : mem_rdata[15:0]; wire [7:0] LOAD_byte = - loadstore_addr[0] ? LOAD_halfword[15:8] : LOAD_halfword[7:0]; + loadstore_addr[0] ? LOAD_halfword[15:8] : LOAD_halfword[7:0]; // STORE @@ -270,7 +271,7 @@ module FemtoRV32( assign mem_wdata[15: 8] = loadstore_addr[0] ? rs2[7:0] : rs2[15: 8]; assign mem_wdata[23:16] = loadstore_addr[1] ? rs2[7:0] : rs2[23:16]; assign mem_wdata[31:24] = loadstore_addr[0] ? rs2[7:0] : - loadstore_addr[1] ? rs2[15:8] : rs2[31:24]; + loadstore_addr[1] ? rs2[15:8] : rs2[31:24]; // The memory write mask: // 1111 if writing a word @@ -280,13 +281,13 @@ module FemtoRV32( // (depending on loadstore_addr[1:0]) wire [3:0] STORE_wmask = - mem_byteAccess ? - (loadstore_addr[1] ? - (loadstore_addr[0] ? 4'b1000 : 4'b0100) : - (loadstore_addr[0] ? 4'b0010 : 4'b0001) + mem_byteAccess ? + (loadstore_addr[1] ? + (loadstore_addr[0] ? 4'b1000 : 4'b0100) : + (loadstore_addr[0] ? 4'b0010 : 4'b0001) ) : - mem_halfwordAccess ? - (loadstore_addr[1] ? 4'b1100 : 4'b0011) : + mem_halfwordAccess ? + (loadstore_addr[1] ? 4'b1100 : 4'b0011) : 4'b1111; /*************************************************************************/ @@ -312,7 +313,7 @@ module FemtoRV32( // register write-back enable. wire writeBack = ~(isBranch | isStore ) & - (state[EXECUTE_bit] | state[WAIT_ALU_OR_MEM_bit]); + (state[EXECUTE_bit] | state[WAIT_ALU_OR_MEM_bit]); // The memory-read signal. assign mem_rstrb = state[EXECUTE_bit] & isLoad | state[FETCH_INSTR_bit]; @@ -326,8 +327,8 @@ module FemtoRV32( wire jumpToPCplusImm = isJAL | (isBranch & predicate); `ifdef NRV_IS_IO_ADDR wire needToWait = isLoad | - isStore & `NRV_IS_IO_ADDR(mem_addr) | - isALU & funct3IsShift; + isStore & `NRV_IS_IO_ADDR(mem_addr) | + isALU & funct3IsShift; `else wire needToWait = isLoad | isStore | isALU & funct3IsShift; `endif @@ -355,7 +356,7 @@ module FemtoRV32( PC <= isJALR ? {aluPlus[ADDR_WIDTH-1:1],1'b0} : jumpToPCplusImm ? PCplusImm : PCplus4; - state <= needToWait ? WAIT_ALU_OR_MEM : FETCH_INSTR; + state <= needToWait ? WAIT_ALU_OR_MEM : FETCH_INSTR; end state[WAIT_ALU_OR_MEM_bit]: begin @@ -363,7 +364,7 @@ module FemtoRV32( end default: begin // FETCH_INSTR - state <= WAIT_INSTR; + state <= WAIT_INSTR; end endcase @@ -417,4 +418,3 @@ endmodule // [2] state uses 1-hot encoding (at any time, state has only one bit set to 1). // It uses a larger number of bits (one bit per state), but often results in // a both more compact (fewer LUTs) and faster state machine. - From 44545000e56665732f28494294a44984d29db85a Mon Sep 17 00:00:00 2001 From: Iztok Jeras Date: Sat, 26 Mar 2022 21:23:20 +0100 Subject: [PATCH 2/7] RTL: dedicated logical operation code fitting in a LUT4 --- FemtoRV/RTL/PROCESSOR/femtorv32_quark.v | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v b/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v index afd5cbcb..6d3b787b 100644 --- a/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v +++ b/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v @@ -135,6 +135,17 @@ module FemtoRV32 #( wire LTU = aluMinus[32]; wire EQ = (aluMinus[31:0] == 0); + // Logical operations + reg [32-1:0] aluLog; + + always @(*) + case (instr[13:12]) + 2'b00 : aluLog = aluIn1 ^ aluIn2; + 2'b10 : aluLog = aluIn1 | aluIn2; + 2'b11 : aluLog = aluIn1 & aluIn2; + default: aluLog = 32'hxxxxxxxx; + endcase + // Notes: // - instr[30] is 1 for SUB and 0 for ADD // - for SUB, need to test also instr[5] to discriminate ADDI: @@ -145,9 +156,9 @@ module FemtoRV32 #( (funct3Is[0] ? instr[30] & instr[5] ? aluMinus[31:0] : aluPlus : 32'b0) | (funct3Is[2] ? {31'b0, LT} : 32'b0) | (funct3Is[3] ? {31'b0, LTU} : 32'b0) | - (funct3Is[4] ? aluIn1 ^ aluIn2 : 32'b0) | - (funct3Is[6] ? aluIn1 | aluIn2 : 32'b0) | - (funct3Is[7] ? aluIn1 & aluIn2 : 32'b0) | + (funct3Is[4] ? aluLog : 32'b0) | + (funct3Is[6] ? aluLog : 32'b0) | + (funct3Is[7] ? aluLog : 32'b0) | (funct3IsShift ? aluReg : 32'b0) ; wire funct3IsShift = funct3Is[1] | funct3Is[5]; From ba6c04fac9c57c2c94c1effffa2f39c1fe4a81b1 Mon Sep 17 00:00:00 2001 From: Iztok Jeras Date: Sun, 27 Mar 2022 17:30:01 +0200 Subject: [PATCH 3/7] RTL: recoded func3 muxes from one-hot to binary, the ALU-mux change made no difference, the BRANCH-mux change saved 2 LC --- FemtoRV/RTL/PROCESSOR/femtorv32_quark.v | 41 +++++++++++++++---------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v b/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v index 6d3b787b..3b5393b6 100644 --- a/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v +++ b/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v @@ -151,15 +151,18 @@ module FemtoRV32 #( // - for SUB, need to test also instr[5] to discriminate ADDI: // (1 for ADD/SUB, 0 for ADDI, and Iimm used by ADDI overlaps bit 30 !) // - instr[30] is 1 for SRA (do sign extension) and 0 for SRL - - wire [31:0] aluOut = - (funct3Is[0] ? instr[30] & instr[5] ? aluMinus[31:0] : aluPlus : 32'b0) | - (funct3Is[2] ? {31'b0, LT} : 32'b0) | - (funct3Is[3] ? {31'b0, LTU} : 32'b0) | - (funct3Is[4] ? aluLog : 32'b0) | - (funct3Is[6] ? aluLog : 32'b0) | - (funct3Is[7] ? aluLog : 32'b0) | - (funct3IsShift ? aluReg : 32'b0) ; + reg [32-1:0] aluOut; + always @(*) + case (instr[14:12]) + 3'b000: aluOut = instr[30] & instr[5] ? aluMinus[31:0] : aluPlus; // ADD + 3'b001: aluOut = aluReg; // SL + 3'b010: aluOut = {31'b0, LT}; // SLT + 3'b011: aluOut = {31'b0, LTU}; // SLTU + 3'b100: aluOut = aluLog; // XOR + 3'b101: aluOut = aluReg; // SR + 3'b110: aluOut = aluLog; // OR + 3'b111: aluOut = aluLog; // AND + endcase wire funct3IsShift = funct3Is[1] | funct3Is[5]; @@ -194,13 +197,19 @@ module FemtoRV32 #( // The predicate for conditional branches. /***************************************************************************/ - wire predicate = - funct3Is[0] & EQ | // BEQ - funct3Is[1] & !EQ | // BNE - funct3Is[4] & LT | // BLT - funct3Is[5] & !LT | // BGE - funct3Is[6] & LTU | // BLTU - funct3Is[7] & !LTU ; // BGEU + reg predicate; + + always @(*) + case (instr[14:12]) + 3'b000: predicate = EQ ; // BEQ + 3'b001: predicate = !EQ ; // BNE + 3'b010: predicate = 1'bx; // + 3'b011: predicate = 1'bx; // + 3'b100: predicate = LT ; // BLT + 3'b101: predicate = !LT ; // BGE + 3'b110: predicate = LTU; // BLTU + 3'b111: predicate = !LTU; // BGEU + endcase /***************************************************************************/ // Program counter and branch target computation. From 446b48361defcd8e28d6d61db08a81e3b46e6e52 Mon Sep 17 00:00:00 2001 From: Iztok Jeras Date: Sun, 27 Mar 2022 20:20:50 +0200 Subject: [PATCH 4/7] RTL: cosmetic change only, no functional change --- FemtoRV/RTL/PROCESSOR/femtorv32_quark.v | 51 ++++++++++++------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v b/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v index 3b5393b6..db216f93 100644 --- a/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v +++ b/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v @@ -38,17 +38,16 @@ module FemtoRV32 #( parameter RESET_ADDR = 32'h00000000, parameter ADDR_WIDTH = 24 )( - input clk, - - output [31:0] mem_addr, // address bus - output [31:0] mem_wdata, // data to be written - output [3:0] mem_wmask, // write mask for the 4 bytes of each word - input [31:0] mem_rdata, // input lines for both data and instr - output mem_rstrb, // active to initiate memory read (used by IO) - input mem_rbusy, // asserted if memory is busy reading value - input mem_wbusy, // asserted if memory is busy writing value - - input reset // set to 0 to reset the processor + input clk, + input reset, // set to 0 to reset the processor + // system bus + output wire [31:0] mem_addr, // address bus + output wire [31:0] mem_wdata, // data to be written + output wire [3:0] mem_wmask, // write mask for the 4 bytes of each word + input wire [31:0] mem_rdata, // input lines for both data and instr + output wire mem_rstrb, // active to initiate memory read (used by IO) + input wire mem_rbusy, // asserted if memory is busy reading value + input wire mem_wbusy // asserted if memory is busy writing value ); /***************************************************************************/ @@ -260,30 +259,30 @@ module FemtoRV32 #( /***************************************************************************/ // All memory accesses are aligned on 32 bits boundary. For this - // reason, we need some circuitry that does unaligned halfword + // reason, we need some circuitry that does unaligned half // and byte load/store, based on: - // - funct3[1:0]: 00->byte 01->halfword 10->word - // - mem_addr[1:0]: indicates which byte/halfword is accessed + // - funct3[1:0]: 00->byte 01->half 10->word + // - mem_addr[1:0]: indicates which byte/half is accessed - wire mem_byteAccess = instr[13:12] == 2'b00; // funct3[1:0] == 2'b00; - wire mem_halfwordAccess = instr[13:12] == 2'b01; // funct3[1:0] == 2'b01; + wire mem_byteAccess = instr[13:12] == 2'b00; // funct3[1:0] == 2'b00; + wire mem_halfAccess = instr[13:12] == 2'b01; // funct3[1:0] == 2'b01; // LOAD, in addition to funct3[1:0], LOAD depends on: // - funct3[2] (instr[14]): 0->do sign expansion 1->no sign expansion wire LOAD_sign = - !instr[14] & (mem_byteAccess ? LOAD_byte[7] : LOAD_halfword[15]); + !instr[14] & (mem_byteAccess ? LOAD_byte[7] : LOAD_half[15]); wire [31:0] LOAD_data = - mem_byteAccess ? {{24{LOAD_sign}}, LOAD_byte} : - mem_halfwordAccess ? {{16{LOAD_sign}}, LOAD_halfword} : - mem_rdata ; + mem_byteAccess ? {{24{LOAD_sign}}, LOAD_byte} : + mem_halfAccess ? {{16{LOAD_sign}}, LOAD_half} : + mem_rdata ; - wire [15:0] LOAD_halfword = + wire [15:0] LOAD_half = loadstore_addr[1] ? mem_rdata[31:16] : mem_rdata[15:0]; wire [7:0] LOAD_byte = - loadstore_addr[0] ? LOAD_halfword[15:8] : LOAD_halfword[7:0]; + loadstore_addr[0] ? LOAD_half[15:8] : LOAD_half[7:0]; // STORE @@ -301,12 +300,12 @@ module FemtoRV32 #( // (depending on loadstore_addr[1:0]) wire [3:0] STORE_wmask = - mem_byteAccess ? + mem_byteAccess ? (loadstore_addr[1] ? - (loadstore_addr[0] ? 4'b1000 : 4'b0100) : - (loadstore_addr[0] ? 4'b0010 : 4'b0001) + (loadstore_addr[0] ? 4'b1000 : 4'b0100) : + (loadstore_addr[0] ? 4'b0010 : 4'b0001) ) : - mem_halfwordAccess ? + mem_halfAccess ? (loadstore_addr[1] ? 4'b1100 : 4'b0011) : 4'b1111; From 9d66a9e3ddbadead5ba18a4ef794177463128586 Mon Sep 17 00:00:00 2001 From: Iztok Jeras Date: Sun, 27 Mar 2022 20:34:30 +0200 Subject: [PATCH 5/7] RTL: changed system bus write data encoder, reduced size a tiny bit --- FemtoRV/RTL/PROCESSOR/femtorv32_quark.v | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v b/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v index db216f93..e2e831c1 100644 --- a/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v +++ b/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v @@ -42,7 +42,7 @@ module FemtoRV32 #( input reset, // set to 0 to reset the processor // system bus output wire [31:0] mem_addr, // address bus - output wire [31:0] mem_wdata, // data to be written + output reg [31:0] mem_wdata, // data to be written output wire [3:0] mem_wmask, // write mask for the 4 bytes of each word input wire [31:0] mem_rdata, // input lines for both data and instr output wire mem_rstrb, // active to initiate memory read (used by IO) @@ -285,12 +285,21 @@ module FemtoRV32 #( loadstore_addr[0] ? LOAD_half[15:8] : LOAD_half[7:0]; // STORE - - assign mem_wdata[ 7: 0] = rs2[7:0]; - assign mem_wdata[15: 8] = loadstore_addr[0] ? rs2[7:0] : rs2[15: 8]; - assign mem_wdata[23:16] = loadstore_addr[1] ? rs2[7:0] : rs2[23:16]; - assign mem_wdata[31:24] = loadstore_addr[0] ? rs2[7:0] : - loadstore_addr[1] ? rs2[15:8] : rs2[31:24]; + always @(*) + case (instr[14:12]) + 3'b000 : case (loadstore_addr[1:0]) + 2'b00: mem_wdata = {8'hxx , 8'hxx , 8'hxx , rs2[ 7: 0]}; + 2'b01: mem_wdata = {8'hxx , 8'hxx , rs2[ 7: 0], 8'hxx }; + 2'b10: mem_wdata = {8'hxx , rs2[ 7: 0], 8'hxx , 8'hxx }; + 2'b11: mem_wdata = {rs2[ 7: 0], 8'hxx , 8'hxx , 8'hxx }; + endcase + 3'b001 : casez (loadstore_addr[1]) + 1'b0 : mem_wdata = {8'hxx , 8'hxx , rs2[15: 8], rs2[ 7: 0]}; + 1'b1 : mem_wdata = {rs2[15: 8], rs2[ 7: 0], 8'hxx , 8'hxx }; + endcase + 3'b010 : mem_wdata = {rs2[31:24], rs2[23:16], rs2[15: 8], rs2[ 7: 0]}; + default: mem_wdata = {8'hxx , 8'hxx , 8'hxx , 8'hxx }; + endcase // The memory write mask: // 1111 if writing a word From 9e9522c471e55082244e064407fbcc7fa8e53156 Mon Sep 17 00:00:00 2001 From: Iztok Jeras Date: Sun, 27 Mar 2022 20:36:14 +0200 Subject: [PATCH 6/7] RTL: fixed issue with memory access alignment --- FemtoRV/RTL/PROCESSOR/femtorv32_quark.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v b/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v index e2e831c1..9a86387b 100644 --- a/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v +++ b/FemtoRV/RTL/PROCESSOR/femtorv32_quark.v @@ -237,7 +237,7 @@ module FemtoRV32 #( // 32 bits, so we deactivate width test for mem_addr and writeBackData assign mem_addr = state[WAIT_INSTR_bit] | state[FETCH_INSTR_bit] ? - PC : loadstore_addr ; + PC : {loadstore_addr[ADDR_WIDTH-1:2], 2'b00} ; /***************************************************************************/ // The value written back to the register file. From 01883929507d69b07f8839b6b46f3ff1ee7723fd Mon Sep 17 00:00:00 2001 From: Iztok Jeras Date: Wed, 24 Dec 2025 00:01:55 +0100 Subject: [PATCH 7/7] initial RISCOF testbench for 'quark' --- FemtoRV/RISCOF/FemtoRV/degu_isa.yaml | 7 + FemtoRV/RISCOF/FemtoRV/degu_platform.yaml | 10 + FemtoRV/RISCOF/FemtoRV/env/link.ld | 18 ++ FemtoRV/RISCOF/FemtoRV/env/model_test.h | 53 ++++ FemtoRV/RISCOF/FemtoRV/quark_isa.yaml | 7 + FemtoRV/RISCOF/FemtoRV/quark_platform.yaml | 10 + FemtoRV/RISCOF/FemtoRV/riscof_r5p.py | 257 +++++++++++++++++++ FemtoRV/RISCOF/Makefile.verilator | 98 +++++++ FemtoRV/RISCOF/build.sh | 19 ++ FemtoRV/RISCOF/config-quark.ini | 28 ++ FemtoRV/RISCOF/riscof_tb.sv | 201 +++++++++++++++ FemtoRV/RISCOF/sail_cSim/env/link.ld | 18 ++ FemtoRV/RISCOF/sail_cSim/env/model_test.h | 55 ++++ FemtoRV/RISCOF/sail_cSim/riscof_sail_cSim.py | 159 ++++++++++++ FemtoRV/RISCOF/spike/env/link.ld | 18 ++ FemtoRV/RISCOF/spike/env/model_test.h | 53 ++++ FemtoRV/RISCOF/spike/riscof_spike.py | 148 +++++++++++ 17 files changed, 1159 insertions(+) create mode 100644 FemtoRV/RISCOF/FemtoRV/degu_isa.yaml create mode 100644 FemtoRV/RISCOF/FemtoRV/degu_platform.yaml create mode 100644 FemtoRV/RISCOF/FemtoRV/env/link.ld create mode 100644 FemtoRV/RISCOF/FemtoRV/env/model_test.h create mode 100644 FemtoRV/RISCOF/FemtoRV/quark_isa.yaml create mode 100644 FemtoRV/RISCOF/FemtoRV/quark_platform.yaml create mode 100644 FemtoRV/RISCOF/FemtoRV/riscof_r5p.py create mode 100644 FemtoRV/RISCOF/Makefile.verilator create mode 100755 FemtoRV/RISCOF/build.sh create mode 100644 FemtoRV/RISCOF/config-quark.ini create mode 100644 FemtoRV/RISCOF/riscof_tb.sv create mode 100644 FemtoRV/RISCOF/sail_cSim/env/link.ld create mode 100644 FemtoRV/RISCOF/sail_cSim/env/model_test.h create mode 100644 FemtoRV/RISCOF/sail_cSim/riscof_sail_cSim.py create mode 100644 FemtoRV/RISCOF/spike/env/link.ld create mode 100644 FemtoRV/RISCOF/spike/env/model_test.h create mode 100644 FemtoRV/RISCOF/spike/riscof_spike.py diff --git a/FemtoRV/RISCOF/FemtoRV/degu_isa.yaml b/FemtoRV/RISCOF/FemtoRV/degu_isa.yaml new file mode 100644 index 00000000..f691af3e --- /dev/null +++ b/FemtoRV/RISCOF/FemtoRV/degu_isa.yaml @@ -0,0 +1,7 @@ +hart_ids: [0] +hart0: + ISA: RV32IC + User_Spec_Version: '2.3' + hw_data_misaligned_support: False + supported_xlen: [32] + physical_addr_sz: 32 diff --git a/FemtoRV/RISCOF/FemtoRV/degu_platform.yaml b/FemtoRV/RISCOF/FemtoRV/degu_platform.yaml new file mode 100644 index 00000000..8e1a3d8e --- /dev/null +++ b/FemtoRV/RISCOF/FemtoRV/degu_platform.yaml @@ -0,0 +1,10 @@ +mtime: + implemented: true + address: 0xbff8 +mtimecmp: + implemented: true + address: 0x4000 +nmi: + label: nmi_vector +reset: + label: reset_vector diff --git a/FemtoRV/RISCOF/FemtoRV/env/link.ld b/FemtoRV/RISCOF/FemtoRV/env/link.ld new file mode 100644 index 00000000..e7337e06 --- /dev/null +++ b/FemtoRV/RISCOF/FemtoRV/env/link.ld @@ -0,0 +1,18 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(rvtest_entry_point) + +SECTIONS +{ + . = 0x80000000; + .text.init : { *(.text.init) } + . = ALIGN(0x1000); + .tohost : { *(.tohost) } + . = ALIGN(0x1000); + .text : { *(.text) } + . = ALIGN(0x1000); + .data : { *(.data) } + .data.string : { *(.data.string) } + .bss : { *(.bss) } + _end = .; +} + diff --git a/FemtoRV/RISCOF/FemtoRV/env/model_test.h b/FemtoRV/RISCOF/FemtoRV/env/model_test.h new file mode 100644 index 00000000..67ce247d --- /dev/null +++ b/FemtoRV/RISCOF/FemtoRV/env/model_test.h @@ -0,0 +1,53 @@ +#ifndef _COMPLIANCE_MODEL_H +#define _COMPLIANCE_MODEL_H +#define RVMODEL_DATA_SECTION \ + .pushsection .tohost,"aw",@progbits; \ + .align 8; .global tohost; tohost: .dword 0; \ + .align 8; .global fromhost; fromhost: .dword 0; \ + .popsection; \ + .align 8; .global begin_regstate; begin_regstate: \ + .word 128; \ + .align 8; .global end_regstate; end_regstate: \ + .word 4; + +//RV_COMPLIANCE_HALT +#define RVMODEL_HALT \ + li x1, 1; \ + write_tohost: \ + sw x1, tohost, t5; \ + j write_tohost; + +#define RVMODEL_BOOT + +//RV_COMPLIANCE_DATA_BEGIN +#define RVMODEL_DATA_BEGIN \ + .align 4; .global begin_signature; begin_signature: + +//RV_COMPLIANCE_DATA_END +#define RVMODEL_DATA_END \ + .align 4; .global end_signature; end_signature: \ + RVMODEL_DATA_SECTION \ + +//RVTEST_IO_INIT +#define RVMODEL_IO_INIT +//RVTEST_IO_WRITE_STR +#define RVMODEL_IO_WRITE_STR(_R, _STR) +//RVTEST_IO_CHECK +#define RVMODEL_IO_CHECK() +//RVTEST_IO_ASSERT_GPR_EQ +#define RVMODEL_IO_ASSERT_GPR_EQ(_S, _R, _I) +//RVTEST_IO_ASSERT_SFPR_EQ +#define RVMODEL_IO_ASSERT_SFPR_EQ(_F, _R, _I) +//RVTEST_IO_ASSERT_DFPR_EQ +#define RVMODEL_IO_ASSERT_DFPR_EQ(_D, _R, _I) + +#define RVMODEL_SET_MSW_INT + +#define RVMODEL_CLEAR_MSW_INT + +#define RVMODEL_CLEAR_MTIMER_INT + +#define RVMODEL_CLEAR_MEXT_INT + + +#endif // _COMPLIANCE_MODEL_H diff --git a/FemtoRV/RISCOF/FemtoRV/quark_isa.yaml b/FemtoRV/RISCOF/FemtoRV/quark_isa.yaml new file mode 100644 index 00000000..49db7d0b --- /dev/null +++ b/FemtoRV/RISCOF/FemtoRV/quark_isa.yaml @@ -0,0 +1,7 @@ +hart_ids: [0] +hart0: + ISA: RV32I + User_Spec_Version: '2.3' + hw_data_misaligned_support: False + supported_xlen: [32] + physical_addr_sz: 32 \ No newline at end of file diff --git a/FemtoRV/RISCOF/FemtoRV/quark_platform.yaml b/FemtoRV/RISCOF/FemtoRV/quark_platform.yaml new file mode 100644 index 00000000..8e1a3d8e --- /dev/null +++ b/FemtoRV/RISCOF/FemtoRV/quark_platform.yaml @@ -0,0 +1,10 @@ +mtime: + implemented: true + address: 0xbff8 +mtimecmp: + implemented: true + address: 0x4000 +nmi: + label: nmi_vector +reset: + label: reset_vector diff --git a/FemtoRV/RISCOF/FemtoRV/riscof_r5p.py b/FemtoRV/RISCOF/FemtoRV/riscof_r5p.py new file mode 100644 index 00000000..41533ff3 --- /dev/null +++ b/FemtoRV/RISCOF/FemtoRV/riscof_r5p.py @@ -0,0 +1,257 @@ +import os +import logging + +import riscof.utils as utils +import riscof.constants as constants +from riscof.pluginTemplate import pluginTemplate + +logger = logging.getLogger() + +class r5p(pluginTemplate): + __model__ = "r5p" + + # TODO: please update the below to indicate family, version, etc of your DUT. + __version__ = "X.X.X" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + config = kwargs.get('config') + + # If the config node for this DUT is missing or empty. Raise an error. + # At minimum we need the paths to the ispec and pspec files. + if config is None: + print("Please enter input file paths in configuration.") + raise SystemExit(1) + + # Number of parallel jobs that can be spawned off by RISCOF + # for various actions performed in later functions, specifically to run the tests in + # parallel on the DUT executable. Can also be used in the build function if required. + self.num_jobs = str(config['jobs'] if 'jobs' in config else 1) + + # Path to the directory where this python file is located. Collect it from the config.ini. + self.pluginpath = os.path.abspath(config['pluginpath']) + + # Collect the paths to the riscv-config absed ISA and platform yaml files. + # One can choose to hardcode these here itself instead of picking it from the config.ini file. + self.isa_spec = os.path.abspath(config['ispec']) + self.platform_spec = os.path.abspath(config['pspec']) + + # We capture if the user would like the run the tests on the target or not. + # If you are interested in just compiling the tests and not running them on the target, + # then following variable should be set to False. + if 'target_run' in config and config['target_run']=='0': + self.target_run = False + else: + self.target_run = True + + # Capture HDL simulator choice. + self.simulator = config['simulator'] + self.dut = config['dut'] + + # Enable/disable debug functionality + self.debug = config['debug'] + + def initialise(self, suite, work_dir, archtest_env): + + # Capture the working directory. + # Any artifacts that the DUT creates should be placed in this directory. + # Other artifacts from the framework and the Reference plugin will also be placed here. + self.work_dir = work_dir + + # Capture the architectural test-suite directory. + self.suite_dir = suite + + # Capture the environment. + self.archtest_env = archtest_env + + # In case of an RTL based DUT, this would be point to the final binary executable of your + # test-bench produced by a simulator (like verilator, vcs, incisive, etc). + # In case of an iss or emulator, this variable could point to where the iss binary is located. + # If PATH variable is missing in the config.ini we can hardcode the alternate here. + # TODO: PATH? + if self.simulator == 'questa': + self.dut_exe = f'DUT={self.dut} make -C {os.path.join(self.work_dir, "../../sim/questa/")} -f Makefile' + elif self.simulator == 'verilator': + self.dut_exe = f'DUT={self.dut} make -C {os.path.join(self.work_dir, "../../sim/verilator/")} -f Makefile' + elif self.simulator == 'vivado': + self.dut_exe = f'DUT={self.dut} make -C {os.path.join(self.work_dir, "../../sim/vivado/")} -f Makefile' + else: + # TODO: __model__ ? + print("No simulator selected for '{__model__}'.") + raise SystemExit(1) + + def build(self, isa_yaml, platform_yaml): + + # load the isa yaml as a dictionary in python. + ispec = utils.load_yaml(isa_yaml)['hart0'] + + # Capture the XLEN value by picking the max value in 'supported_xlen' field of isa YAML. + # This will be useful in setting integer value in the compiler string (if not already hardcoded); + self.xlen = ('64' if 64 in ispec['supported_xlen'] else '32') + + # For DUT start building the '--isa' argument. + # The self.isa is DUT specific and may not be useful for all DUTs. + self.isa = 'rv' + self.xlen + for ext in ['I', 'M', 'C', 'F', 'D']: + if ext in ispec["ISA"]: + self.isa += ext.lower() + + # Note the march is not hardwired here, because it will change for each test. + # Similarly the output elf name and compile macros will be assigned later in the runTests function. + self.compile_exe = f'riscv{self.xlen}-unknown-elf-gcc' + self.objcopy_exe = f'riscv{self.xlen}-unknown-elf-objcopy' + self.objdump_exe = f'riscv{self.xlen}-unknown-elf-objdump' + self.symbols_exe = f'riscv{self.xlen}-unknown-elf-nm' + + def runTests(self, testList): + + # TUDO: figure out why there is an extra character in the name. + name = self.name[:-1] + + # Delete Makefile if it already exists. + if os.path.exists(self.work_dir+ "/Makefile." + name): + os.remove(self.work_dir+ "/Makefile." + name) + # create an instance the makeUtil class that we will use to create targets. + make = utils.makeUtil(makefilePath=os.path.join(self.work_dir, "Makefile." + name)) + + # Set the make command that will be used. + # The num_jobs parameter was set in the __init__ function earlier + make.makeCommand = 'make -k -j' + self.num_jobs + + # We will iterate over each entry in the testList. + # Each entry node will be referred to by the variable testname. + for testname in testList: + + # For each testname we get all its fields (as described by the testList format). + testentry = testList[testname] + + # We capture the path to the assembly file of this test. + test = testentry['test_path'] + + # Capture the directory where the artifacts of this test will be dumped/created. + # RISCOF is going to look into this directory for the signature files. + test_dir = testentry['work_dir'] + + # Name of the elf file after compilation of the test. + elf = 'dut.elf' + + # Name of the signature file as per requirement of RISCOF. + # RISCOF expects the signature to be named as DUT-.signature. + # The below variable creates an absolute path of signature file. + sig_file = os.path.join(test_dir, name + ".signature") + + # Name of the HDL testbench log file + trace_file = os.path.join(test_dir, "dut.log") + + # For each test there are specific compile macros that need to be enabled. + # The macros in the testList node only contain the macros/values. + # For the gcc toolchain we need to prefix with "-D". The following does precisely that. + compile_macros = ' '.join([f'-D{macro}' for macro in testentry['macros']]) + + # Construct the command line for compiling testcase source assembly into an elf file. + compile_cmd = self.compile_exe + ( + f' -mabi={'lp64' if (self.xlen == 64) else 'ilp32'}' + f' -march={testentry['isa'].lower()}' + f' -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -g' + f' -T {self.pluginpath}/env/link.ld' + f' -I {self.pluginpath}/env/' + f' -I {self.archtest_env}' + f' {compile_macros}' + f' {test} -o {elf}' + ) + + # Command for converting elf file into a binary/hex file for loading into HDL testbench memory. + # Uncomment either the binary or hex version, depending on your. + objcopy_cmd = self.objcopy_exe + f' -O binary {elf} {elf}.bin' + #objcopy_cmd = self.objcopy_exe + f' -O binary {elf} {elf}.hex' + + # Disassemble the ELF file for debugging purposes + objdump_cmd = self.objdump_exe + ( + f' -M no-aliases -M numeric' + f' -D {elf} > {elf}.disass' + ) + + # extract listed symbols + symbols_list = ['begin_signature', 'end_signature', 'tohost', 'fromhost'] + # construct dictionary of listed symbols + symbols_cmd = [] + # get symbol list from elf file + cmd = self.symbols_exe + f' {elf} > dut.symbols' + symbols_cmd.append(cmd) + for symbol in symbols_list: + # get symbols from symbol list file + cmd = f'{symbol}=$$(grep -w {symbol} dut.symbols | cut -c 1-8)' + symbols_cmd.append(cmd) + + # Simulation define macros. + simulate_defines_dict = {} + if self.debug: + simulate_defines_dict.update({'TRACE_SPIKE': None}) + + # Convert define macro dictionary into CLI + # TODO: properly handle define macros without value + if self.simulator == 'questa': + simulate_defines = ' '.join([f'-defineall {key}={val}' for key, val in simulate_defines_dict.items()]) + elif self.simulator == 'verilator': + simulate_defines = ' '.join([f'-D{key}={val}' for key, val in simulate_defines_dict.items()]) + elif self.simulator == 'vivado': + simulate_defines = ' '.join([f'-d {key}={val}' for key, val in simulate_defines_dict.items()]) + + # Construct Verilog plusargs dictionary containing file paths. + simulate_plusargs_dict = { + 'firmware': os.path.join(test_dir, elf)+'.bin', + 'signature': sig_file, + 'trace': trace_file + } + + # provide ELF symbols as plusargs + for symbol in symbols_list: + simulate_plusargs_dict.update({symbol: f'$${symbol}'}) + + # Other DUT testbench specific Verilog plusargs can be added here. + simulate_plusargs_dict.update({}) + + # Convert Verilog plusargs dictionary into CLI + if self.simulator in ('questa', 'verilator'): + simulate_plusargs = ' '.join([f'+{key}={val}' for key, val in simulate_plusargs_dict.items()]) + elif self.simulator == 'vivado': + simulate_plusargs = ' '.join([f'-testplusarg {key}={val}' for key, val in simulate_plusargs_dict.items()]) + + # If the user wants to disable running the tests and only compile the tests, + # then the "else" clause is executed below assigning the sim command to simple no action echo statement. + if self.target_run: + # set up the simulation command. Template is for spike. Please change. + simulate_cmd = self.dut_exe + ( + f' RISCOF_DEFINES="{simulate_defines}"' + f' RISCOF_PLUSARGS="{simulate_plusargs}"' + ) + else: + simulate_cmd = 'echo "NO RUN"' + + # Concatenate all commands that need to be executed within a make-target. + execute = [] + execute.append(f'cd {test_dir}') + execute.append(compile_cmd) + execute.append(objcopy_cmd) + execute.append(objdump_cmd) + execute += symbols_cmd + execute.append(simulate_cmd) + + # Create a target. + # The makeutil will create a target with the name "TARGET" where + # num starts from 0 and increments automatically for each new target that is added. + make.add_target('@' + ';\\\n'.join(execute)) + + # If you would like to exit the framework once the makefile generation is complete uncomment the following line. + # Note this will prevent any signature checking or report generation. + #raise SystemExit + + # Once the make-targets are done and the makefile has been created, + # run all the targets in parallel using the make command set above. + make.execute_all(self.work_dir) + + # If target runs are not required then we simply exit as this point after running all the makefile targets. + if not self.target_run: + raise SystemExit(0) + diff --git a/FemtoRV/RISCOF/Makefile.verilator b/FemtoRV/RISCOF/Makefile.verilator new file mode 100644 index 00000000..3a6d39d9 --- /dev/null +++ b/FemtoRV/RISCOF/Makefile.verilator @@ -0,0 +1,98 @@ +################################################################################ +# HDL source files +################################################################################ + +VERILATOR?=verilator +VERILATOR_COVERAGE?=verilator_coverage + +################################################################################ +# HDL source files +################################################################################ + +# Verilog/SystemVerilog RTL +RTL+=../RTL/PROCESSOR/femtorv32_quark.v + +# SystemVerilog bench (Test SV) +TSV+=riscof_tb.sv + +# combined HDL sources +HDL =${RTL} +HDL+=${TSV} + +# top level file +TOP ?= riscof_tb + +################################################################################ +# Verilator compiler/linker flags +################################################################################ + +# specify SystemVerilog LRM version +#VFLAGS += +1800-2012ext+sv +# Generate binary executable +VFLAGS += --binary +# Optimize +VFLAGS += -O3 +# Number of CPU threads +VFLAGS += -j 0 +# optimize for speed or maximize finding X assign issues +VFLAGS += --x-assign fast +#VFLAGS += --x-assign 0 +#VFLAGS += --x-assign 1 +#VFLAGS += --x-assign unique +# Warn about lint issues; may not want this on less solid designs +#VFLAGS += -Wall +# Check SystemVerilog assertions +VFLAGS += --assert +# Generate coverage analysis +#VFLAGS += --coverage + +# Run Verilator in debug mode +#VFLAGS += --debug +# Add this trace to get a backtrace in gdb +#VFLAGS += --gdbbt + +# Make waveforms +#ifdef TRACE +VFLAGS += --trace-fst +VFLAGS += --trace-structs +#endif + +################################################################################ +# Verilog macros +################################################################################ + +# set VERILATOR macro (used to handle tool quirks) +#MCR += -DVERILATOR +ifdef TRACE +MCR += -DTRACE_DEBUG +endif + +################################################################################ +# Verilog toplevel parameter override +################################################################################ + +# set XLEN parameter +#XLEN ?= 32 +#PAR += -GXLEN=${XLEN} + +################################################################################ +# Verilog $plusargs runtime arguments +################################################################################ + +ARG += +test_dir="${FILE_MEM}" +="${FILE_SIG}" + +################################################################################ +# +################################################################################ + +all: sim + +lint: ${HDL} + ${VERILATOR} --lint-only ${HDL} --top ${TOP} + +compile: ${HDL} + ${VERILATOR} ${VFLAGS} ${MCR} ${PAR} --top ${TOP} ${HDL} + +sim: compile + obj_dir/V${TOP} ${ARG} +# ${VERILATOR_COVERAGE} --annotate logs/annotated logs/coverage.dat diff --git a/FemtoRV/RISCOF/build.sh b/FemtoRV/RISCOF/build.sh new file mode 100755 index 00000000..05b8ae25 --- /dev/null +++ b/FemtoRV/RISCOF/build.sh @@ -0,0 +1,19 @@ +#! /usr/bin/env bash + +# setup python environment +python3 -m venv .venv +source .venv/bin/activate +pip3 install riscv_isac git+https://github.com/riscv-non-isa/riscv-arch-test/#subdirectory=riscv-isac +pip3 install riscv-config git+https://github.com/riscv-software-src/riscv-config@dev +pip3 install riscof git+https://github.com/riscv-software-src/riscof@dev + +# setup tool paths +source ../settings-questa.sh +source ../settings-vivado.sh +export PATH=/opt/riscv-isa-sim/bin/:$PATH +export PATH=/opt/riscv-gcc/bin/:$PATH +export PATH=`git rev-parse --show-toplevel`/submodules/sail-riscv/build/c_emulator/:$PATH + +# run tests for mouse/degu +riscof run --config=config-mouse.ini --suite=../submodules/riscv-arch-test/riscv-test-suite/ --env=../submodules/riscv-arch-test/riscv-test-suite/env +riscof run --config=config-degu.ini --suite=../submodules/riscv-arch-test/riscv-test-suite/ --env=../submodules/riscv-arch-test/riscv-test-suite/env \ No newline at end of file diff --git a/FemtoRV/RISCOF/config-quark.ini b/FemtoRV/RISCOF/config-quark.ini new file mode 100644 index 00000000..76064844 --- /dev/null +++ b/FemtoRV/RISCOF/config-quark.ini @@ -0,0 +1,28 @@ +[RISCOF] +#ReferencePlugin=sail_cSim +#ReferencePluginPath=sail_cSim +ReferencePlugin=spike +ReferencePluginPath=spike +# shared RISCOF plugin for all DUT +DUTPlugin=FemtoRV +DUTPluginPath=FemtoRV + +[FemtoRV] +dut=quark +# path to env folder +pluginpath=FemtoRV +# paths to isa/platform YAML files +ispec=FemtoRV/quark_isa.yaml +pspec=FemtoRV/quark_platform.yaml +# choice of supported simulator (questa/verilator/vivado) +#simulator=questa +simulator=verilator +#simulator=vivado +debug=True +target_run=1 + +[sail_cSim] +pluginpath=sail_cSim + +[spike] +pluginpath=spike diff --git a/FemtoRV/RISCOF/riscof_tb.sv b/FemtoRV/RISCOF/riscof_tb.sv new file mode 100644 index 00000000..567473d9 --- /dev/null +++ b/FemtoRV/RISCOF/riscof_tb.sv @@ -0,0 +1,201 @@ +//////////////////////////////////////////////////////////////////////////////// +// FemtoRV RISCOF testbench +// FemtoRV32 as DUT +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2025 Iztok Jeras +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/////////////////////////////////////////////////////////////////////////////// + +module riscof_tb #( + // memory parameters + parameter int unsigned MEM_BASE = 32'h8000_0000, + parameter int unsigned MEM_SIZE = 32'h0020_0000, + // global constants + localparam int unsigned XLEN = 32 +)(); + + // runtime arguments + string test_dir; + int unsigned tohost; + int unsigned fromhost; + int unsigned begin_signature; + int unsigned end_signature; + + // system signals + logic clk = 1'b1; // clock + logic rst = 1'b1; // reset + + // system bus + logic [XLEN -1:0] mem_addr ; // address bus + logic [XLEN/8-1:0][8-1:0] mem_wdata; // data to be written + logic [XLEN/8-1:0] mem_wmask; // write mask for the 4 bytes of each word + logic [XLEN/8-1:0][8-1:0] mem_rdata; // input lines for both data and instr + logic mem_rstrb; // active to initiate memory read (used by IO) + logic mem_rbusy; // asserted if memory is busy reading value + logic mem_wbusy; // asserted if memory is busy writing value + + // memory array + logic [8-1:0] memory_array [0:MEM_SIZE-1]; + + //////////////////////////////////////////////////////////////////////////////// + // RTL DUT instance + //////////////////////////////////////////////////////////////////////////////// + + FemtoRV32 #( + .RESET_ADDR (32'h8000_0000), + .ADDR_WIDTH (XLEN) + ) DUT ( + .clk ( clk), + .reset (~rst), // set to 0 to reset the processor + .mem_addr (mem_addr ), // address bus + .mem_wdata (mem_wdata), // data to be written + .mem_wmask (mem_wmask), // write mask for the 4 bytes of each word + .mem_rdata (mem_rdata), // input lines for both data and instr + .mem_rstrb (mem_rstrb), // active to initiate memory read (used by IO) + .mem_rbusy (mem_rbusy), // asserted if memory is busy reading value + .mem_wbusy (mem_wbusy) // asserted if memory is busy writing value + ); + + //////////////////////////////////////////////////////////////////////////////// + // memory + //////////////////////////////////////////////////////////////////////////////// + + // memory array + always_ff @(posedge clk) + begin + // write access + for (int unsigned i=0; i 0) begin + $display("RISCOF: loaded firmware file \'%s\' into memory.", firmware); + end else begin + $fatal ("RISCOF: firmware file \'%s\' not found.", firmware); + end + // RESET sequence + repeat (4) @(posedge clk); + rst <= 1'b0; + // wait for HTIF halt request + signature = {test_dir, "signature.hex"}; + do begin + @(posedge clk); + if (&mem_wmask && (mem_addr == 32'h0000_0010)) begin + htif_halt <= mem_wdata[0][0]; + end + if (htif_halt) begin + assert (memory_dump_hex(signature, begin_signature-MEM_BASE, end_signature-MEM_BASE) > 0) begin + $display("RISCOF: saved signature from 0x%8h to 0x%8h into file \'%s\'", begin_signature, end_signature, signature); + end else begin + $fatal ("RISCOF: could not save signature file \'%s\'.", signature); + end + end + end while (!htif_halt); + // wait a few clock cycles before finishing simulation + repeat(4) @(posedge clk); + $finish; + end + /* verilator lint_on INITIALDLY */ + + //////////////////////////////////////////////////////////////////////////////// + // Verbose execution trace + //////////////////////////////////////////////////////////////////////////////// + +endmodule: riscof_tb diff --git a/FemtoRV/RISCOF/sail_cSim/env/link.ld b/FemtoRV/RISCOF/sail_cSim/env/link.ld new file mode 100644 index 00000000..e7337e06 --- /dev/null +++ b/FemtoRV/RISCOF/sail_cSim/env/link.ld @@ -0,0 +1,18 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(rvtest_entry_point) + +SECTIONS +{ + . = 0x80000000; + .text.init : { *(.text.init) } + . = ALIGN(0x1000); + .tohost : { *(.tohost) } + . = ALIGN(0x1000); + .text : { *(.text) } + . = ALIGN(0x1000); + .data : { *(.data) } + .data.string : { *(.data.string) } + .bss : { *(.bss) } + _end = .; +} + diff --git a/FemtoRV/RISCOF/sail_cSim/env/model_test.h b/FemtoRV/RISCOF/sail_cSim/env/model_test.h new file mode 100644 index 00000000..386ffdfa --- /dev/null +++ b/FemtoRV/RISCOF/sail_cSim/env/model_test.h @@ -0,0 +1,55 @@ +#ifndef _COMPLIANCE_MODEL_H +#define _COMPLIANCE_MODEL_H + +#define RVMODEL_DATA_SECTION \ + .pushsection .tohost,"aw",@progbits; \ + .align 8; .global tohost; tohost: .dword 0; \ + .align 8; .global fromhost; fromhost: .dword 0; \ + .popsection; \ + .align 8; .global begin_regstate; begin_regstate: \ + .word 128; \ + .align 8; .global end_regstate; end_regstate: \ + .word 4; + +//RV_COMPLIANCE_HALT +#define RVMODEL_HALT \ + li x1, 1; \ + write_tohost: \ + sw x1, tohost, t5; \ + j write_tohost; + +#define RVMODEL_BOOT + +//RV_COMPLIANCE_DATA_BEGIN +#define RVMODEL_DATA_BEGIN \ + RVMODEL_DATA_SECTION \ + .align 4;\ + .global begin_signature; begin_signature: + +//RV_COMPLIANCE_DATA_END +#define RVMODEL_DATA_END \ + .align 4; .global end_signature; end_signature: + +//RVTEST_IO_INIT +#define RVMODEL_IO_INIT +//RVTEST_IO_WRITE_STR +#define RVMODEL_IO_WRITE_STR(_R, _STR) +//RVTEST_IO_CHECK +#define RVMODEL_IO_CHECK() +//RVTEST_IO_ASSERT_GPR_EQ +#define RVMODEL_IO_ASSERT_GPR_EQ(_S, _R, _I) +//RVTEST_IO_ASSERT_SFPR_EQ +#define RVMODEL_IO_ASSERT_SFPR_EQ(_F, _R, _I) +//RVTEST_IO_ASSERT_DFPR_EQ +#define RVMODEL_IO_ASSERT_DFPR_EQ(_D, _R, _I) + +#define RVMODEL_SET_MSW_INT + +#define RVMODEL_CLEAR_MSW_INT + +#define RVMODEL_CLEAR_MTIMER_INT + +#define RVMODEL_CLEAR_MEXT_INT + + +#endif // _COMPLIANCE_MODEL_H diff --git a/FemtoRV/RISCOF/sail_cSim/riscof_sail_cSim.py b/FemtoRV/RISCOF/sail_cSim/riscof_sail_cSim.py new file mode 100644 index 00000000..b44583fb --- /dev/null +++ b/FemtoRV/RISCOF/sail_cSim/riscof_sail_cSim.py @@ -0,0 +1,159 @@ +import os +import re +import shutil +import subprocess +import shlex +import logging +import random +import string +from string import Template + +import riscof.utils as utils +import riscof.constants as constants +from riscof.pluginTemplate import pluginTemplate +from riscv_isac.isac import isac + +logger = logging.getLogger() + +class sail_cSim(pluginTemplate): + __model__ = "sail_c_simulator" + __version__ = "0.5.0" + + def __init__(self, *args, **kwargs): + """ + Process config.ini file and setup logging. + """ + sclass = super().__init__(*args, **kwargs) + + config = kwargs.get('config') + if config is None: + logger.error(self.name + " config node is missing.") + raise SystemExit(1) + self.num_jobs = str(config['jobs'] if 'jobs' in config else 1) + self.pluginpath = os.path.abspath(config['pluginpath']) + self.isa_spec = os.path.abspath(config['ispec']) if 'ispec' in config else '' + self.platform_spec = os.path.abspath(config['pspec']) if 'ispec' in config else '' + self.make = config['make'] if 'make' in config else 'make' + logger.debug(self.name + " plugin initialized using the following configuration:") + self.config = config + for entry in config: + logger.debug(entry+' : '+config[entry]) + return sclass + + def initialise(self, suite, work_dir, archtest_env): + """ + Prepare toolchain, and model executables. + Checking whether the executables are available is done in the build step, + since this step lacks XLEN information from YAML files. + """ + self.suite = suite + self.work_dir = work_dir + + # NOTE: The following assumes you are using the riscv-gcc toolchain. + # If not please change appropriately. + # prepare toolchain executables ({} to be replaced with XLEN) + self.objdump_exe = 'riscv{}-unknown-elf-objdump' + self.compile_exe = 'riscv{}-unknown-elf-gcc' + + # prepare toolchain command template + self.objdump_cmd = self.objdump_exe + ' -D {} > {}' + self.compile_cmd = self.compile_exe + ( + ' -march={}' + ' -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles' + ' -T ' + self.pluginpath + '/env/link.ld' + ' -I ' + self.pluginpath + '/env/' + ' -I ' + archtest_env + ) + + # prepare model executable + self.ref_exe = os.path.join(self.config['PATH'] if 'PATH' in self.config else "", "riscv_sim_rv{}d") + + def build(self, isa_yaml, platform_yaml): + """ + Build is run only once before running for each test list. + """ + # load ISA YAML file + ispec = utils.load_yaml(isa_yaml)['hart0'] + + self.xlen = ('64' if 64 in ispec['supported_xlen'] else '32') + # construct ISA string + self.isa = 'rv' + self.xlen + for ext in ['I', 'M', 'C', 'F', 'D']: + if ext in ispec["ISA"]: + self.isa += ext.lower() + + # NOTE: The following assumes you are using the riscv-gcc toolchain. + # If not please change appropriately. + # extend compiler command with ABI (integer and floating-point calling convention) + self.compile_cmd = self.compile_cmd + ' -mabi=ilp'+self.xlen + + # check if toolchain executables are available + for tool in [self.compile_exe, self.objdump_exe]: + tool_xlen = tool.format(self.xlen) + if shutil.which(tool_xlen) is None: + logger.error(tool_xlen + ": executable not found. Please check environment setup.") + raise SystemExit(1) + + # check if the reference executable is available + if shutil.which(self.ref_exe.format(self.xlen)) is None: + logger.error(self.ref_exe.format(self.xlen) + ": executable not found. Please check environment setup.") + raise SystemExit(1) + + # check if 'make' is available + if shutil.which(self.make) is None: + logger.error(self.make + ": executable not found. Please check environment setup.") + raise SystemExit(1) + + def runTests(self, testList, cgf_file=None): + # remove ':' from the end of the name + name = self.name[:-1] + if os.path.exists(self.work_dir+ "/Makefile." + name): + os.remove(self.work_dir+ "/Makefile." + name) + make = utils.makeUtil(makefilePath=os.path.join(self.work_dir, "Makefile." + name)) + make.makeCommand = self.make + ' -j' + self.num_jobs + for file in testList: + testentry = testList[file] + test = testentry['test_path'] + test_dir = testentry['work_dir'] + test_name = test.rsplit('/',1)[1][:-2] + + elf = 'ref.elf' + + execute = '' + + # prepare list of commands to execute + cmd = "@cd " + testentry['work_dir'] + execute += f'{cmd};\\\n' + + # NOTE: The following assumes you are using the riscv-gcc toolchain. + # If not please change appropriately. + # compile testcase assembly into an elf file + cmd = self.compile_cmd.format(self.xlen, testentry['isa'].lower()) + ( + f' {test}' + f' -o {elf}' + f' -D{" -D".join(testentry['macros'])}' + ) + execute += f'{cmd};\\\n' + + # dump disassembled elf file + cmd = self.objdump_cmd.format(self.xlen, elf, 'ref.disass') + execute += f'{cmd};\\\n' + + # run reference model + cmd = self.ref_exe.format(self.xlen) + f' --test-signature={name}.signature {elf} > ref.log 2>&1' + execute += f'{cmd};\\\n' + + cov_str = ' ' + for label in testentry['coverage_labels']: + cov_str += ' -l ' + label + + if cgf_file is not None: + cmd = f'riscv_isac --verbose info coverage -d \ + -t {test_name}.log --parser-name c_sail -o coverage.rpt \ + --sig-label begin_signature end_signature \ + --test-label rvtest_code_begin rvtest_code_end \ + -e ref.elf -c {' -c '.join(cgf_file)} -x{self.xlen} {cov_str}' + execute += f'{cmd};\\\n' + + make.add_target(execute) + make.execute_all(self.work_dir) diff --git a/FemtoRV/RISCOF/spike/env/link.ld b/FemtoRV/RISCOF/spike/env/link.ld new file mode 100644 index 00000000..e7337e06 --- /dev/null +++ b/FemtoRV/RISCOF/spike/env/link.ld @@ -0,0 +1,18 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(rvtest_entry_point) + +SECTIONS +{ + . = 0x80000000; + .text.init : { *(.text.init) } + . = ALIGN(0x1000); + .tohost : { *(.tohost) } + . = ALIGN(0x1000); + .text : { *(.text) } + . = ALIGN(0x1000); + .data : { *(.data) } + .data.string : { *(.data.string) } + .bss : { *(.bss) } + _end = .; +} + diff --git a/FemtoRV/RISCOF/spike/env/model_test.h b/FemtoRV/RISCOF/spike/env/model_test.h new file mode 100644 index 00000000..67ce247d --- /dev/null +++ b/FemtoRV/RISCOF/spike/env/model_test.h @@ -0,0 +1,53 @@ +#ifndef _COMPLIANCE_MODEL_H +#define _COMPLIANCE_MODEL_H +#define RVMODEL_DATA_SECTION \ + .pushsection .tohost,"aw",@progbits; \ + .align 8; .global tohost; tohost: .dword 0; \ + .align 8; .global fromhost; fromhost: .dword 0; \ + .popsection; \ + .align 8; .global begin_regstate; begin_regstate: \ + .word 128; \ + .align 8; .global end_regstate; end_regstate: \ + .word 4; + +//RV_COMPLIANCE_HALT +#define RVMODEL_HALT \ + li x1, 1; \ + write_tohost: \ + sw x1, tohost, t5; \ + j write_tohost; + +#define RVMODEL_BOOT + +//RV_COMPLIANCE_DATA_BEGIN +#define RVMODEL_DATA_BEGIN \ + .align 4; .global begin_signature; begin_signature: + +//RV_COMPLIANCE_DATA_END +#define RVMODEL_DATA_END \ + .align 4; .global end_signature; end_signature: \ + RVMODEL_DATA_SECTION \ + +//RVTEST_IO_INIT +#define RVMODEL_IO_INIT +//RVTEST_IO_WRITE_STR +#define RVMODEL_IO_WRITE_STR(_R, _STR) +//RVTEST_IO_CHECK +#define RVMODEL_IO_CHECK() +//RVTEST_IO_ASSERT_GPR_EQ +#define RVMODEL_IO_ASSERT_GPR_EQ(_S, _R, _I) +//RVTEST_IO_ASSERT_SFPR_EQ +#define RVMODEL_IO_ASSERT_SFPR_EQ(_F, _R, _I) +//RVTEST_IO_ASSERT_DFPR_EQ +#define RVMODEL_IO_ASSERT_DFPR_EQ(_D, _R, _I) + +#define RVMODEL_SET_MSW_INT + +#define RVMODEL_CLEAR_MSW_INT + +#define RVMODEL_CLEAR_MTIMER_INT + +#define RVMODEL_CLEAR_MEXT_INT + + +#endif // _COMPLIANCE_MODEL_H diff --git a/FemtoRV/RISCOF/spike/riscof_spike.py b/FemtoRV/RISCOF/spike/riscof_spike.py new file mode 100644 index 00000000..ff582892 --- /dev/null +++ b/FemtoRV/RISCOF/spike/riscof_spike.py @@ -0,0 +1,148 @@ +import os +import re +import shutil +import subprocess +import shlex +import logging +import random +import string +from string import Template + +import riscof.utils as utils +import riscof.constants as constants +from riscof.pluginTemplate import pluginTemplate + +logger = logging.getLogger() + +class spike(pluginTemplate): + __model__ = "spike" + __version__ = "0.1.0" + + def __init__(self, *args, **kwargs): + """ + Process config.ini file and setup logging. + """ + sclass = super().__init__(*args, **kwargs) + + config = kwargs.get('config') + if config is None: + logger.error(self.name + " config node is missing.") + raise SystemExit(1) + self.num_jobs = str(config['jobs'] if 'jobs' in config else 1) + self.pluginpath = os.path.abspath(config['pluginpath']) + self.isa_spec = os.path.abspath(config['ispec']) if 'ispec' in config else '' + self.platform_spec = os.path.abspath(config['pspec']) if 'ispec' in config else '' + self.make = config['make'] if 'make' in config else 'make' + logger.debug(self.name + " plugin initialized using the following configuration:") + self.config = config + for entry in config: + logger.debug(entry+' : '+config[entry]) + return sclass + + def initialise(self, suite, work_dir, archtest_env): + """ + Prepare toolchain, and model executables. + Checking whether the executables are available is done in the build step, + since this step lacks XLEN information from YAML files. + """ + self.suite = suite + self.work_dir = work_dir + + # NOTE: The following assumes you are using the riscv-gcc toolchain. + # If not please change appropriately. + # prepare toolchain executables ({} to be replaced with XLEN) + self.objdump_exe = 'riscv{}-unknown-elf-objdump' + self.compile_exe = 'riscv{}-unknown-elf-gcc' + + # prepare toolchain command template + self.objdump_cmd = self.objdump_exe + ' -M no-aliases -M numeric -D {} > {}' + self.compile_cmd = self.compile_exe + ( + ' -march={}' + ' -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles' + ' -T ' + self.pluginpath + '/env/link.ld' + ' -I ' + self.pluginpath + '/env/' + ' -I ' + archtest_env + ) + + # prepare model executable + self.ref_exe = os.path.join(self.config['PATH'] if 'PATH' in self.config else "","spike") + + def build(self, isa_yaml, platform_yaml): + """ + Build is run only once before running for each test list. + """ + # load ISA YAML file + ispec = utils.load_yaml(isa_yaml)['hart0'] + + self.xlen = ('64' if 64 in ispec['supported_xlen'] else '32') + # construct ISA string + self.isa = 'rv' + self.xlen + for ext in ['I', 'M', 'C', 'F', 'D']: + if ext in ispec["ISA"]: + self.isa += ext.lower() + + # NOTE: The following assumes you are using the riscv-gcc toolchain. + # If not please change appropriately. + # extend compiler command with ABI (integer and floating-point calling convention) + self.compile_cmd = self.compile_cmd+' -mabi='+('lp64 ' if 64 in ispec['supported_xlen'] else 'ilp32 ') + + # check if toolchain executables are available + for tool in [self.compile_exe, self.objdump_exe]: + tool_xlen = tool.format(self.xlen) + if shutil.which(tool_xlen) is None: + logger.error(tool_xlen + ": executable not found. Please check environment setup.") + raise SystemExit(1) + + # check if the reference executable is available + if shutil.which(self.ref_exe.format(self.xlen)) is None: + logger.error(self.ref_exe.format(self.xlen) + ": executable not found. Please check environment setup.") + raise SystemExit(1) + + # check if 'make' is available + if shutil.which(self.make) is None: + logger.error(self.make + ": executable not found. Please check environment setup.") + raise SystemExit(1) + + def runTests(self, testList, cgf_file=None): + # remove ':' from the end of the name + name = self.name[:-1] + if os.path.exists(self.work_dir+ "/Makefile." + name): + os.remove(self.work_dir+ "/Makefile." + name) + make = utils.makeUtil(makefilePath=os.path.join(self.work_dir, "Makefile." + name)) + make.makeCommand = self.make + ' -j' + self.num_jobs + for file in testList: + testentry = testList[file] + test = testentry['test_path'] + test_dir = testentry['work_dir'] + test_name = test.rsplit('/',1)[1][:-2] + + elf = 'ref.elf' + + execute = '' + + # prepare list of commands to execute + cmd = "@cd " + testentry['work_dir'] + execute += f'{cmd};\\\n' + + # NOTE: The following assumes you are using the riscv-gcc toolchain. + # If not please change appropriately. + # compile testcase assembly into an elf file + cmd = self.compile_cmd.format(self.xlen, testentry['isa'].lower()) + ( + f' {test}' + f' -o {elf}' + f' -D{" -D".join(testentry['macros'])}' + ) + execute += f'{cmd};\\\n' + + # dump disassembled elf file + cmd = self.objdump_cmd.format(self.xlen, elf, 'ref.disass') + execute += f'{cmd};\\\n' + + # run reference model + # signature-granularity specifies how many bytes in HEX are in a line of the signature file + cmd = self.ref_exe + f' --isa={self.isa} --log-commits +signature={name}.signature +signature-granularity=4 {elf} > ref.log 2>&1' + execute += f'{cmd};\\\n' + + + make.add_target(execute) + make.execute_all(self.work_dir)