mirror of
https://github.com/openhwgroup/cvw.git
synced 2025-04-24 13:57:07 -04:00
Started adding asynchronous TIMECLK for CLINT
This commit is contained in:
parent
9d4e1671c9
commit
9693110857
12 changed files with 169 additions and 54 deletions
|
@ -1 +1 @@
|
|||
Subproject commit be67c99bd461742aa1c100bcc0732657faae2230
|
||||
Subproject commit 307c77b26e070ae85ffea665ad9b642b40e33c86
|
|
@ -29,8 +29,8 @@
|
|||
|
||||
rv32i_sc_tests = \
|
||||
WALLY-MMU-SV32 \
|
||||
WALLY-PMA \
|
||||
WALLY-PMP
|
||||
# WALLY-PMA \
|
||||
|
||||
rv32i_tests = $(addsuffix .elf, $(rv32i_sc_tests))
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,21 +82,23 @@ 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;
|
||||
MTIME <= HWDATA;
|
||||
end else MTIME <= MTIME + 1;
|
||||
end else begin:clint // 32-bit
|
||||
always @(posedge HCLK) begin
|
||||
|
@ -112,25 +114,27 @@ 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;
|
||||
MTIME[63:32]<= HWDATA;
|
||||
end else MTIME <= MTIME + 1;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue