From 9693110857fb45f10e08605ac97096fc9180a230 Mon Sep 17 00:00:00 2001 From: David Harris Date: Sun, 2 Jan 2022 21:18:16 +0000 Subject: [PATCH] Started adding asynchronous TIMECLK for CLINT --- addins/riscv-arch-test | 2 +- .../rv32i_m/privilege/Makefrag | 2 +- .../rv64i_m/privilege/Makefrag | 4 +- wally-pipelined/src/privileged/csrc.sv | 6 + wally-pipelined/src/uncore/clint.sv | 135 +++++++++++++++--- wally-pipelined/src/uncore/uncore.sv | 3 +- .../src/wally/wallypipelinedsoc.sv | 3 +- .../src/wally/wallypipelinedsocwrapper.v | 54 +++---- .../testbench/testbench-coremark_bare.sv | 2 +- wally-pipelined/testbench/testbench-fpga.sv | 2 +- wally-pipelined/testbench/testbench-linux.sv | 2 +- wally-pipelined/testbench/testbench.sv | 8 +- 12 files changed, 169 insertions(+), 54 deletions(-) diff --git a/addins/riscv-arch-test b/addins/riscv-arch-test index be67c99bd..307c77b26 160000 --- a/addins/riscv-arch-test +++ b/addins/riscv-arch-test @@ -1 +1 @@ -Subproject commit be67c99bd461742aa1c100bcc0732657faae2230 +Subproject commit 307c77b26e070ae85ffea665ad9b642b40e33c86 diff --git a/tests/wally-riscv-arch-test/riscv-test-suite/rv32i_m/privilege/Makefrag b/tests/wally-riscv-arch-test/riscv-test-suite/rv32i_m/privilege/Makefrag index 80a76eecb..3400d7507 100644 --- a/tests/wally-riscv-arch-test/riscv-test-suite/rv32i_m/privilege/Makefrag +++ b/tests/wally-riscv-arch-test/riscv-test-suite/rv32i_m/privilege/Makefrag @@ -29,8 +29,8 @@ rv32i_sc_tests = \ WALLY-MMU-SV32 \ - WALLY-PMA \ WALLY-PMP +# WALLY-PMA \ rv32i_tests = $(addsuffix .elf, $(rv32i_sc_tests)) diff --git a/tests/wally-riscv-arch-test/riscv-test-suite/rv64i_m/privilege/Makefrag b/tests/wally-riscv-arch-test/riscv-test-suite/rv64i_m/privilege/Makefrag index dd5367702..1f61a7417 100644 --- a/tests/wally-riscv-arch-test/riscv-test-suite/rv64i_m/privilege/Makefrag +++ b/tests/wally-riscv-arch-test/riscv-test-suite/rv64i_m/privilege/Makefrag @@ -30,8 +30,8 @@ rv64i_sc_tests = \ WALLY-MMU-SV39 \ WALLY-MMU-SV48 \ - WALLY-PMA \ - WALLY-PMP +# WALLY-PMP +# WALLY-PMA \ rv64i_tests = $(addsuffix .elf, $(rv64i_sc_tests)) diff --git a/wally-pipelined/src/privileged/csrc.sv b/wally-pipelined/src/privileged/csrc.sv index bbf96a10e..6130551ac 100644 --- a/wally-pipelined/src/privileged/csrc.sv +++ b/wally-pipelined/src/privileged/csrc.sv @@ -30,6 +30,7 @@ module csrc #(parameter MHPMCOUNTERBASE = 12'hB00, MHPMCOUNTERHBASE = 12'hB80, + MHPMEVENTBASE = 12'h320, HPMCOUNTERBASE = 12'hC00, HPMCOUNTERHBASE = 12'hC80, TIME = 12'hC01, @@ -162,3 +163,8 @@ module csrc #(parameter endgenerate endmodule +// To Do: +// review couunter spec +// upper unimplemented counters should read as 0 rather than illegal access +// mounteren should only exist if u-mode exists +// Implement MHPMEVENT diff --git a/wally-pipelined/src/uncore/clint.sv b/wally-pipelined/src/uncore/clint.sv index 0b084ef24..3f0d61e44 100644 --- a/wally-pipelined/src/uncore/clint.sv +++ b/wally-pipelined/src/uncore/clint.sv @@ -27,7 +27,7 @@ `include "wally-config.vh" module clint ( - input logic HCLK, HRESETn, + input logic HCLK, HRESETn, TIMECLK, input logic HSELCLINT, input logic [15:0] HADDR, input logic HWRITE, @@ -52,7 +52,7 @@ module clint ( flopr #(16) entrydflop(HCLK, ~HRESETn, entry, entryd); assign HRESPCLINT = 0; // OK - assign HREADYCLINT = 1'b1; // will need to be modified if CLINT ever needs more than 1 cycle to do something + assign HREADYCLINT = 1'b1; // *** needs to depend on DONE during accesses // word aligned reads generate @@ -70,7 +70,7 @@ module clint ( // register access generate - if (`XLEN==64) begin:clint + if (`XLEN==64) begin:clint // 64-bit always @(posedge HCLK) begin case(entry) 16'h0000: HREADCLINT <= {63'b0, MSIP}; @@ -82,22 +82,24 @@ module clint ( always_ff @(posedge HCLK or negedge HRESETn) if (~HRESETn) begin MSIP <= 0; - MTIMECMP <= (64)'(0); + MTIMECMP <= 0; // MTIMECMP is not reset end else if (memwrite) begin if (entryd == 16'h0000) MSIP <= HWDATA[0]; if (entryd == 16'h4000) MTIMECMP <= HWDATA; - // MTIME Counter. Eventually change this to run off separate clock. Synchronization then needed end + // eventually replace MTIME logic below with timereg + // timereg tr(HCLK, HRESETn, TIMECLK, memwrite & (entryd==16'hBFF8), 1'b0, HWDATA, MTIME, done); + always_ff @(posedge HCLK or negedge HRESETn) if (~HRESETn) begin MTIME <= 0; // MTIMECMP is not reset end else if (memwrite & entryd == 16'hBFF8) begin // MTIME Counter. Eventually change this to run off separate clock. Synchronization then needed - MTIME <= HWDATA; - end else MTIME <= MTIME + 1; + MTIME <= HWDATA; + end else MTIME <= MTIME + 1; end else begin:clint // 32-bit always @(posedge HCLK) begin case(entry) @@ -112,26 +114,28 @@ module clint ( always_ff @(posedge HCLK or negedge HRESETn) if (~HRESETn) begin MSIP <= 0; - MTIMECMP <= (64)'(0); - // MTIMECMP is not reset + MTIMECMP <= 0; + // MTIMECMP is not reset ***? end else if (memwrite) begin if (entryd == 16'h0000) MSIP <= HWDATA[0]; if (entryd == 16'h4000) MTIMECMP[31:0] <= HWDATA; if (entryd == 16'h4004) MTIMECMP[63:32] <= HWDATA; // MTIME Counter. Eventually change this to run off separate clock. Synchronization then needed - end + end +// eventually replace MTIME logic below with timereg + // timereg tr(HCLK, HRESETn, TIMECLK, memwrite & (entryd==16'hBFF8), memwrite & (entryd == 16'hBFFC), HWDATA, MTIME, done); always_ff @(posedge HCLK or negedge HRESETn) if (~HRESETn) begin MTIME <= 0; // MTIMECMP is not reset - end else if (memwrite & (entryd == 16'hBFF8)) begin - MTIME[31:0] <= HWDATA; - end else if (memwrite & (entryd == 16'hBFFC)) begin + end else if (memwrite & (entryd == 16'hBFF8)) begin + MTIME[31:0] <= HWDATA; + end else if (memwrite & (entryd == 16'hBFFC)) begin // MTIME Counter. Eventually change this to run off separate clock. Synchronization then needed - MTIME[63:32]<= HWDATA; - end else MTIME <= MTIME + 1; - end + MTIME[63:32]<= HWDATA; + end else MTIME <= MTIME + 1; + end endgenerate // Software interrupt when MSIP is set @@ -141,3 +145,102 @@ module clint ( endmodule +module timeregsync( + input logic clk, resetn, + input logic we0, we1, + input logic [`XLEN-1:0] wd, + output logic [63:0] q); + + if (`XLEN==64) + always_ff @(posedge clk or negedge resetn) + if (~resetn) q <= 0; + else if (we0) q <= wd; + else q <= q + 1; + else + always_ff @(posedge clk or negedge resetn) + if (~resetn) q <= 0; + else if (we0) q[31:0] <= wd; + else if (we1) q[63:32] <= wd; + else q <= q + 1; +endmodule + +module timereg( + input logic HCLK, HRESETn, TIMECLK, + input logic we0, we1, + input logic [`XLEN-1:0] HWDATA, + output logic [63:0] MTIME, + output logic done); + +// if (`TIMEBASE_SYNC) begin:timereg // use HCLK for MTIME + if (1) begin:timereg // use HCLK for MTIME + timregsync timeregsync(.clk(HCLK), .resetn(HRESETn), .we0, .we1, .wd(HWDATA), .q(MTIME)); + assign done = 1; // immediately completes + end else begin // use asynchronous TIMECLK + // TIME counter runs on TIMECLK but bus interface runs on HCLK + // Need to synchronize reads and writes + // This is subtle because synchronizing a binary counter on a per-bit basis could give a mix of old and new bits + // Instead, we use a Gray coded counter that only changes one bit per cycle + // Synchronizing this for a read is safe because we are guaranteed to get either the old or the new value. + // Writing to the counter requires a request/acknowledge handshake to ensure the write value is held long enough. + // The handshake signals are synchronized in each direction across the interface + // There is no back pressure on instructions, so if multiple counter writes occur *** + + logic req, req_sync, ack, we0_stored, we1_stored, ack_stored, resetn_sync; + logic [`XLEN-1:0] wd_stored; + logic [63:0] time_int, time_int_gc, time_gc, MTIME_GC; + + // When a write enable is asserted for a cycle, sample the enables and data and raise a request until it is acknowledged + // When the acknowledge falls, the transaction is done and the system is ready for another write. + // ***look at redoing this assuming write enable and data are held rather than pulsed. + always_ff @(posedge HCLK or negedge HRESETn) + if (~HRESETn) + req <= 0; // don't bother resetting wd + else begin + req <= we0 | we1 | req & ~ack; + we0_stored <= we0; + we1_stored <= we1; + wd_stored <= HWDATA; + ack_stored <= ack; + done <= ack_stored & ~ack; + end + + // synchronize the reset and reqest into the TIMECLK domain + sync resetsync(TIMECLK, HRESETn, resetn_sync); + sync rsync(TIMECLK, req, req_sync); + // synchronize the acknowledge back to the HCLK domain to indicate the request was handled and can be lowered + sync async(HCLK, req_sync, ack); + + timeregsync timeregsync(.clk(TIMECLK), .resetn(resetn_sync), .we0(we0_stored), .we1(we1_stored), .wd(wd_stored), .q(time_int)); + binarytogray b2g(time_int, time_int_gc); + flop gcreg(TIMECLK, time_int_gc, time_gc); + + sync timesync[63:0](HCLK, time_gc, MTIME_GC); + graytobinary g2b(MTIME_GC, MTIME); + end +endmodule + +module binarytogray #(parameter N = `XLEN) ( + input logic [N-1:0] b, + output logic [N-1:0] g); + + // G[N-1] = B[N-1]; G[i] = B[i] ^ B[i+1] for 0 <= i < N-1 + // requires single layer of N-1 XOR gates + assign g = b ^ {1'b0, b[N-1:1]}; +endmodule + +module graytobinary #(parameter N = `XLEN) ( + input logic [N-1:0] g, + output logic [N-1:0] b); + + // B[N-1] = G[N-1]; B[i] = G[i] ^ B[i+1] for 0 <= i < N-1 + // requires rippling through N-1 XOR gates + generate + begin + genvar i; + assign b[N-1] = g[N-1]; + for (i=N-2; i >= 0; i--) begin:g2b + assign b[i] = g[i] ^ b[i+1]; + end + end + endgenerate +endmodule diff --git a/wally-pipelined/src/uncore/uncore.sv b/wally-pipelined/src/uncore/uncore.sv index ce01e3143..85d1404a2 100644 --- a/wally-pipelined/src/uncore/uncore.sv +++ b/wally-pipelined/src/uncore/uncore.sv @@ -31,6 +31,7 @@ module uncore ( // AHB Bus Interface input logic HCLK, HRESETn, + input logic TIMECLK, input logic [31:0] HADDR, input logic [`AHBW-1:0] HWDATAIN, input logic HWRITE, @@ -115,7 +116,7 @@ module uncore ( // memory-mapped I/O peripherals if (`CLINT_SUPPORTED == 1) begin : clint clint clint( - .HCLK, .HRESETn, + .HCLK, .HRESETn, .TIMECLK, .HSELCLINT, .HADDR(HADDR[15:0]), .HWRITE, .HWDATA, .HREADY, .HTRANS, .HREADCLINT, diff --git a/wally-pipelined/src/wally/wallypipelinedsoc.sv b/wally-pipelined/src/wally/wallypipelinedsoc.sv index 16f28b32f..1a176e557 100644 --- a/wally-pipelined/src/wally/wallypipelinedsoc.sv +++ b/wally-pipelined/src/wally/wallypipelinedsoc.sv @@ -51,6 +51,7 @@ module wallypipelinedsoc ( output logic HMASTLOCK, output logic HREADY, // I/O Interface + input logic TIMECLK, input logic [31:0] GPIOPinsIn, output logic [31:0] GPIOPinsOut, GPIOPinsEn, input logic UARTSin, @@ -85,7 +86,7 @@ module wallypipelinedsoc ( .HADDRD, .HSIZED, .HWRITED ); - uncore uncore(.HCLK, .HRESETn, + uncore uncore(.HCLK, .HRESETn, .TIMECLK, .HADDR, .HWDATAIN(HWDATA), .HWRITE, .HSIZE, .HBURST, .HPROT, .HTRANS, .HMASTLOCK, .HRDATAEXT, .HREADYEXT, .HRESPEXT, .HRDATA, .HREADY, .HRESP, .HADDRD, .HSIZED, .HWRITED, .TimerIntM, .SwIntM, .ExtIntM, .GPIOPinsIn, .GPIOPinsOut, .GPIOPinsEn, .UARTSin, .UARTSout, .MTIME_CLINT, diff --git a/wally-pipelined/src/wally/wallypipelinedsocwrapper.v b/wally-pipelined/src/wally/wallypipelinedsocwrapper.v index f6f9446dc..75641147e 100644 --- a/wally-pipelined/src/wally/wallypipelinedsocwrapper.v +++ b/wally-pipelined/src/wally/wallypipelinedsocwrapper.v @@ -50,6 +50,7 @@ module wallypipelinedsocwrapper ( output HMASTLOCK, output HREADY, // I/O Interface + input TIMECLK, input [3:0] GPIOPinsIn_IO, output [4:0] GPIOPinsOut_IO, input UARTSin, @@ -89,32 +90,33 @@ module wallypipelinedsocwrapper ( // wrapper for fpga wallypipelinedsoc wallypipelinedsoc - (.clk(clk), + (.clk, .reset_ext(reset), - .HRDATAEXT(HRDATAEXT), - .HREADYEXT(HREADYEXT), - .HRESPEXT(HRESPEXT), - .HSELEXT(HSELEXT), - .HCLK(HCLK), - .HRESETn(HRESETn), - .HADDR(HADDR), - .HWDATA(HWDATA), - .HWRITE(HWRITE), - .HSIZE(HSIZE), - .HBURST(HBURST), - .HPROT(HPROT), - .HTRANS(HTRANS), - .HMASTLOCK(HMASTLOCK), - .HREADY(HREADY), - .GPIOPinsIn(GPIOPinsIn), - .GPIOPinsOut(GPIOPinsOut), - .GPIOPinsEn(GPIOPinsEn), - .UARTSin(UARTSin), - .UARTSout(UARTSout), - .SDCDatIn(SDCDatIn), - .SDCCLK(SDCCLK), - .SDCCmdIn(SDCCmdIn), - .SDCCmdOut(SDCCmdOut), - .SDCCmdOE(SDCCmdOE)); + .HRDATAEXT, + .HREADYEXT, + .HRESPEXT, + .HSELEXT, + .HCLK, + .HRESETn, + .HADDR, + .HWDATA, + .HWRITE, + .HSIZE, + .HBURST, + .HPROT, + .HTRANS, + .HMASTLOCK, + .HREADY, + .TIMECLK, + .GPIOPinsIn, + .GPIOPinsOut, + .GPIOPinsEn, + .UARTSin, + .UARTSout, + .SDCDatIn, + .SDCCLK, + .SDCCmdIn, + .SDCCmdOut, + .SDCCmdOE); endmodule diff --git a/wally-pipelined/testbench/testbench-coremark_bare.sv b/wally-pipelined/testbench/testbench-coremark_bare.sv index 32cd0f900..99e9cf3d6 100644 --- a/wally-pipelined/testbench/testbench-coremark_bare.sv +++ b/wally-pipelined/testbench/testbench-coremark_bare.sv @@ -74,7 +74,7 @@ module testbench(); assign HRDATAEXT = 0; wallypipelinedsoc dut(.clk, .reset_ext, .HRDATAEXT,.HREADYEXT, .HRESPEXT,.HSELEXT, .HCLK, .HRESETn, .HADDR, .HWDATA, .HWRITE, .HSIZE, .HBURST, .HPROT, - .HTRANS, .HMASTLOCK, .HREADY, .GPIOPinsIn, .GPIOPinsOut, .GPIOPinsEn, + .HTRANS, .HMASTLOCK, .HREADY, .TIMECLK(0), .GPIOPinsIn, .GPIOPinsOut, .GPIOPinsEn, .UARTSin, .UARTSout, .SDCCmdIn, .SDCCmdOut, .SDCCmdOE, .SDCDatIn, .SDCCLK); logic [31:0] InstrW; diff --git a/wally-pipelined/testbench/testbench-fpga.sv b/wally-pipelined/testbench/testbench-fpga.sv index 17f2a1d31..02d8cc99d 100644 --- a/wally-pipelined/testbench/testbench-fpga.sv +++ b/wally-pipelined/testbench/testbench-fpga.sv @@ -602,7 +602,7 @@ string tests32f[] = '{ wallypipelinedsocwrapper dut(.clk, .reset_ext, .HRDATAEXT,.HREADYEXT, .HRESPEXT,.HSELEXT, .HCLK, .HRESETn, .HADDR, .HWDATA, .HWRITE, .HSIZE, .HBURST, .HPROT, - .HTRANS, .HMASTLOCK, .HREADY, .GPIOPinsIn, .GPIOPinsOut, .GPIOPinsEn, + .HTRANS, .HMASTLOCK, .HREADY, .TIMECLK(0), .GPIOPinsIn, .GPIOPinsOut, .GPIOPinsEn, .UARTSin, .UARTSout, .SDCCmdIn, .SDCCmdOut, .SDCCmdOE, .SDCDatIn, .SDCCLK); // Track names of instructions diff --git a/wally-pipelined/testbench/testbench-linux.sv b/wally-pipelined/testbench/testbench-linux.sv index 811ac5e54..877d8f918 100644 --- a/wally-pipelined/testbench/testbench-linux.sv +++ b/wally-pipelined/testbench/testbench-linux.sv @@ -82,7 +82,7 @@ module testbench(); .HRDATAEXT, .HREADYEXT, .HREADY, .HSELEXT, .HRESPEXT, .HCLK, .HRESETn, .HADDR, .HWDATA, .HWRITE, .HSIZE, .HBURST, .HPROT, .HTRANS, .HMASTLOCK, - .GPIOPinsIn, .GPIOPinsOut, .GPIOPinsEn, + .TIMECLK(0), .GPIOPinsIn, .GPIOPinsOut, .GPIOPinsEn, .UARTSin, .UARTSout, .SDCCLK, .SDCCmdIn, .SDCCmdOut, .SDCCmdOE, .SDCDatIn); diff --git a/wally-pipelined/testbench/testbench.sv b/wally-pipelined/testbench/testbench.sv index 70ed1236b..97d712326 100644 --- a/wally-pipelined/testbench/testbench.sv +++ b/wally-pipelined/testbench/testbench.sv @@ -152,7 +152,7 @@ logic [3:0] dummy; wallypipelinedsoc dut(.clk, .reset_ext, .reset, .HRDATAEXT,.HREADYEXT, .HRESPEXT,.HSELEXT, .HCLK, .HRESETn, .HADDR, .HWDATA, .HWRITE, .HSIZE, .HBURST, .HPROT, - .HTRANS, .HMASTLOCK, .HREADY, .GPIOPinsIn, .GPIOPinsOut, .GPIOPinsEn, + .HTRANS, .HMASTLOCK, .HREADY, .TIMECLK(1'b0), .GPIOPinsIn, .GPIOPinsOut, .GPIOPinsEn, .UARTSin, .UARTSout, .SDCCmdIn, .SDCCmdOut, .SDCCmdOE, .SDCDatIn, .SDCCLK); // Track names of instructions @@ -332,9 +332,9 @@ logic [3:0] dummy; endmodule module riscvassertions; - // Legal number of PMP entries are 0, 16, or 64 initial begin assert (`PMP_ENTRIES == 0 || `PMP_ENTRIES==16 || `PMP_ENTRIES==64) else $error("Illegal number of PMP entries: PMP_ENTRIES must be 0, 16, or 64"); + assert (`S_SUPPORTED || `MEM_VIRTMEM == 0) else $error("Virtual memory requires S mode support"); assert (`DIV_BITSPERCYCLE == 1 || `DIV_BITSPERCYCLE==2 || `DIV_BITSPERCYCLE==4) else $error("Illegal number of divider bits/cycle: DIV_BITSPERCYCLE must be 1, 2, or 4"); assert (`F_SUPPORTED || ~`D_SUPPORTED) else $error("Can't support double (D) without supporting float (F)"); assert (`XLEN == 64 || ~`D_SUPPORTED) else $error("Wally does not yet support D extensions on RV32"); @@ -351,7 +351,9 @@ module riscvassertions; assert (2**$clog2(`ITLB_ENTRIES) == `ITLB_ENTRIES || `MEM_VIRTMEM==0) else $error("ITLB_ENTRIES must be a power of 2"); assert (2**$clog2(`DTLB_ENTRIES) == `DTLB_ENTRIES || `MEM_VIRTMEM==0) else $error("DTLB_ENTRIES must be a power of 2"); assert (`RAM_RANGE >= 56'h07FFFFFF) else $warning("Some regression tests will fail if RAM_RANGE is less than 56'h07FFFFFF"); - assert (`ZICSR_SUPPORTED == 1 || (`PMP_ENTRIES == 0 && `MEM_VIRTMEM == 0)) else $error("PMP_ENTRIES and MEM_VIRTMEM must be zero if ZICSR not supported."); + assert (`ZICSR_SUPPORTED == 1 || (`PMP_ENTRIES == 0 && `MEM_VIRTMEM == 0)) else $error("PMP_ENTRIES and MEM_VIRTMEM must be zero if ZICSR not supported."); + assert (`ZICSR_SUPPORTED == 1 || (`S_SUPPORTED == 0 && `U_SUPPORTED == 0)) else $error("S and U modes not supported if ZISR not supported"); + assert (`U_SUPPORTED || (`S_SUPPORTED == 0)) else $error ("S mode only supported if U also is supported"); end endmodule