372 lines
12 KiB
Plaintext
372 lines
12 KiB
Plaintext
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved
|
|
|
|
package Timer;
|
|
|
|
// ================================================================
|
|
// This package implements a slave IP with two unrelated pieces of
|
|
// RISC-V functionality:
|
|
//
|
|
// - real-time timer:
|
|
// Two 64-bit memory-mapped registers (rg_time and rg_timecmp).
|
|
// Delivers an external interrupt whenever rg_timecmp >= rg_time.
|
|
// The timer is cleared when rg_timecmp is written.
|
|
// Can be used for the RISC-V v1.10 Privilege Spec 'mtime' and
|
|
// 'mtimecmp', and provides a memory-mapped API to access them.
|
|
//
|
|
// Offset/Size Name Function
|
|
// 'h_4000/8 Bytes mtimecmp R/W the hart0 mtimecmp register
|
|
// 'h_BFF8/8 Bytes mtime R/W the mtime register
|
|
//
|
|
// - Memory-mapped location for software interrupts.
|
|
//
|
|
// Offset/Size Name Function
|
|
// 'h_0000/8 Bytes msip R/W Writing LSB=1 generates a software interrupt to hart0
|
|
//
|
|
// ----------------
|
|
// This slave IP can be attached to fabrics with 32b- or 64b-wide data channels.
|
|
// (NOTE: this is the width of the fabric, which can be chosen
|
|
// independently of the native width of a CPU master on the
|
|
// fabric (such as RV32/RV64 for a RISC-V CPU).
|
|
// When attached to 32b-wide fabric, 64-bit locations must be
|
|
// read/written in two 32b transaction, once for the lower 32b and
|
|
// once for the upper 32b.
|
|
//
|
|
// Some of the 'truncate()'s and 'zeroExtend()'s below are no-ops but
|
|
// necessary to satisfy type-checking.
|
|
// ================================================================
|
|
|
|
export Timer_IFC (..), mkTimer;
|
|
|
|
// ================================================================
|
|
// BSV library imports
|
|
|
|
import Vector :: *;
|
|
import FIFOF :: *;
|
|
import GetPut :: *;
|
|
import ClientServer :: *;
|
|
import ConfigReg :: *;
|
|
|
|
// ----------------
|
|
// BSV additional libs
|
|
|
|
import Cur_Cycle :: *;
|
|
import GetPut_Aux :: *;
|
|
import Semi_FIFOF :: *;
|
|
import ByteLane :: *;
|
|
|
|
// ================================================================
|
|
// Project imports
|
|
|
|
import Fabric_Defs :: *;
|
|
import AXI4_Lite_Types :: *;
|
|
|
|
// ================================================================
|
|
// Local constants and types
|
|
|
|
// Module state
|
|
typedef enum {MODULE_STATE_START, MODULE_STATE_READY } Module_State
|
|
deriving (Bits, Eq, FShow);
|
|
|
|
// ================================================================
|
|
// Interface
|
|
|
|
interface Timer_IFC;
|
|
// Reset
|
|
interface Server #(Bit #(0), Bit #(0)) server_reset;
|
|
|
|
// set_addr_map should be called after this module's reset
|
|
method Action set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);
|
|
|
|
// Main Fabric Reqs/Rsps
|
|
interface AXI4_Lite_Slave_IFC #(Wd_Addr, Wd_Data, Wd_User) slave;
|
|
|
|
// Timer interrupt
|
|
// True/False = set/clear interrupt-pending in CPU's MTIP
|
|
interface Get #(Bool) get_timer_interrupt_req;
|
|
|
|
// Software interrupt
|
|
interface Get #(Bool) get_sw_interrupt_req;
|
|
endinterface
|
|
|
|
// ================================================================
|
|
|
|
(* synthesize *)
|
|
module mkTimer (Timer_IFC);
|
|
|
|
// Verbosity: 0: quiet; 1: reset; 2: timer interrupts, all reads and writes
|
|
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
|
|
|
|
Reg #(Module_State) rg_state <- mkReg (MODULE_STATE_START);
|
|
Reg #(Fabric_Addr) rg_addr_base <- mkRegU;
|
|
Reg #(Fabric_Addr) rg_addr_lim <- mkRegU;
|
|
|
|
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
|
|
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
|
|
|
|
// ----------------
|
|
// Connector to fabric
|
|
|
|
AXI4_Lite_Slave_Xactor_IFC #(Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Lite_Slave_Xactor;
|
|
|
|
// ----------------
|
|
// Timer registers
|
|
|
|
Reg #(Bit #(64)) crg_time [2] <- mkCReg (2, 1);
|
|
Reg #(Bit #(64)) crg_timecmp [2] <- mkCReg (2, 0);
|
|
|
|
Reg #(Bool) rg_mtip <- mkReg (True);
|
|
|
|
// Timer interrupt queue
|
|
FIFOF #(Bool) f_timer_interrupt_req <- mkFIFOF;
|
|
|
|
// ----------------
|
|
// Software-interrupt registers
|
|
|
|
Reg #(Bool) rg_msip <- mkRegU;
|
|
|
|
// Software interrupt queue
|
|
FIFOF #(Bool) f_sw_interrupt_req <- mkFIFOF;
|
|
|
|
// ================================================================
|
|
// BEHAVIOR
|
|
|
|
// ----------------------------------------------------------------
|
|
// Reset
|
|
|
|
// ns: 06/12/17 -- GDB reset bug fix
|
|
// The explicit condition was preventing the Timer from being reset by GDB
|
|
// after the initial hardware reset. This meant that on issuing a reset
|
|
// command from GDB, control was never returned by hardware. The explicit
|
|
// condition is not necessary as on a GDB reset, it's okay if the Timer
|
|
// returns to its reset state irrespective of its current state
|
|
// rule rl_reset (rg_state == MODULE_STATE_START);
|
|
rule rl_reset;
|
|
f_reset_reqs.deq;
|
|
slave_xactor.reset;
|
|
f_timer_interrupt_req.clear;
|
|
f_sw_interrupt_req.clear;
|
|
|
|
rg_state <= MODULE_STATE_READY;
|
|
crg_time [1] <= 1;
|
|
crg_timecmp [1] <= 0;
|
|
rg_mtip <= True;
|
|
rg_msip <= False;
|
|
|
|
f_reset_rsps.enq (?);
|
|
|
|
if (cfg_verbosity != 0)
|
|
$display ("%0d: Timer.rl_reset", cur_cycle);
|
|
endrule
|
|
|
|
// ----------------------------------------------------------------
|
|
// Keep time and generate interrupt
|
|
|
|
// Increment time, but saturate, do not wrap-around
|
|
(* fire_when_enabled, no_implicit_conditions *)
|
|
rule rl_tick_timer ((rg_state == MODULE_STATE_READY) && (crg_time [0] != '1));
|
|
crg_time [0] <= crg_time [0] + 1;
|
|
endrule
|
|
|
|
// Compare and generate timer interrupt request
|
|
Bool new_mtip = (crg_time [0] >= crg_timecmp [0]);
|
|
rule rl_compare ((rg_state == MODULE_STATE_READY)
|
|
&& (rg_mtip != new_mtip));
|
|
rg_mtip <= new_mtip;
|
|
f_timer_interrupt_req.enq (new_mtip);
|
|
if (cfg_verbosity > 1)
|
|
$display ("%0d: Timer.rl_compare: new MTIP = %0d, time = %0d, timecmp = %0d",
|
|
cur_cycle, new_mtip, crg_time [0], crg_timecmp [0]);
|
|
endrule
|
|
|
|
// ----------------------------------------------------------------
|
|
// Handle fabric read requests
|
|
|
|
rule rl_process_rd_req (rg_state == MODULE_STATE_READY);
|
|
let rda <- pop_o (slave_xactor.o_rd_addr);
|
|
if (cfg_verbosity > 1) begin
|
|
$display ("%0d: Timer.rl_process_rd_req: rg_mtip = %0d", cur_cycle, rg_mtip);
|
|
$display (" ", fshow (rda));
|
|
end
|
|
|
|
let byte_addr = rda.araddr - rg_addr_base;
|
|
|
|
Bit #(Wd_Data) rdata = 0;
|
|
AXI4_Lite_Resp rresp = AXI4_LITE_OKAY;
|
|
|
|
case (byte_addr)
|
|
'h_0000: rdata = zeroExtend (rg_msip ? 1'b1 : 1'b0);
|
|
'h_4000: rdata = truncate (crg_timecmp [0]); // truncates for 32b fabrics
|
|
'h_BFF8: rdata = truncate (crg_time [0]); // truncates for 32b fabrics
|
|
|
|
// The following ALIGN4B reads are only needed for 32b fabrics
|
|
'h_0004: rdata = 0;
|
|
'h_4004: rdata = zeroExtend (crg_timecmp [0] [63:32]); // extends for 64b fabrics
|
|
'h_BFFC: rdata = zeroExtend (crg_time [0] [63:32]); // extends for 64b fabrics
|
|
default: begin
|
|
rresp = AXI4_LITE_SLVERR;
|
|
|
|
$display ("%0d: ERROR: Timer.rl_process_rd_req: unrecognized addr", cur_cycle);
|
|
$display (" ", fshow (rda));
|
|
end
|
|
endcase
|
|
|
|
let rdr = AXI4_Lite_Rd_Data {rresp: rresp, rdata: rdata, ruser: rda.aruser};
|
|
slave_xactor.i_rd_data.enq (rdr);
|
|
|
|
if (cfg_verbosity > 1) begin
|
|
$display (" <= ", fshow (rdr));
|
|
end
|
|
endrule
|
|
|
|
// ----------------------------------------------------------------
|
|
// Handle fabric write requests
|
|
|
|
rule rl_process_wr_req (rg_state == MODULE_STATE_READY);
|
|
let wra <- pop_o (slave_xactor.o_wr_addr);
|
|
let wrd <- pop_o (slave_xactor.o_wr_data);
|
|
|
|
if (cfg_verbosity > 1) begin
|
|
$display ("%0d: Timer.rl_process_wr_req: rg_mtip = %0d", cur_cycle, rg_mtip);
|
|
$display (" ", fshow (wra));
|
|
$display (" ", fshow (wrd));
|
|
end
|
|
|
|
let byte_addr = wra.awaddr - rg_addr_base;
|
|
|
|
AXI4_Lite_Resp bresp = AXI4_LITE_OKAY;
|
|
|
|
case (byte_addr)
|
|
'h_0000: begin
|
|
Bool new_msip = (wrd.wdata [0] == 1'b1);
|
|
if (rg_msip != new_msip) begin
|
|
rg_msip <= new_msip;
|
|
f_sw_interrupt_req.enq (new_msip);
|
|
if (cfg_verbosity > 1)
|
|
$display (" new MSIP = %0d", new_msip);
|
|
end
|
|
end
|
|
'h_4000: begin
|
|
Bit #(64) old_timecmp = crg_timecmp [1];
|
|
Bit #(64) new_timecmp = fn_update_strobed_bytes (old_timecmp,
|
|
zeroExtend (wrd.wdata),
|
|
zeroExtend (wrd.wstrb));
|
|
crg_timecmp [1] <= new_timecmp;
|
|
|
|
if (cfg_verbosity > 1) begin
|
|
$display (" Writing MTIMECMP");
|
|
$display (" old MTIMECMP = 0x%0h", old_timecmp);
|
|
$display (" new MTIMECMP = 0x%0h", new_timecmp);
|
|
$display (" cur MTIME = 0x%0h", crg_time [1]);
|
|
$display (" new MTIMECMP - MTIME = 0x%0h", new_timecmp - crg_time [1]);
|
|
end
|
|
end
|
|
'h_BFF8: begin
|
|
Bit #(64) old_time = crg_time [1];
|
|
Bit #(64) new_time = fn_update_strobed_bytes (old_time,
|
|
zeroExtend (wrd.wdata),
|
|
zeroExtend (wrd.wstrb));
|
|
crg_time [1] <= new_time;
|
|
|
|
if (cfg_verbosity > 1) begin
|
|
$display (" Writing MTIME");
|
|
$display (" old MTIME = 0x%0h", old_time);
|
|
$display (" new MTIME = 0x%0h", new_time);
|
|
end
|
|
end
|
|
|
|
// The following ALIGN4B writes are only needed for 32b fabrics
|
|
'h_0004: noAction;
|
|
'h_4004: begin
|
|
Bit #(64) old_timecmp = crg_timecmp [1];
|
|
Bit #(64) new_timecmp = fn_update_strobed_bytes (old_timecmp,
|
|
{ wrd.wdata [31:0], 32'h0 },
|
|
{ wrd.wstrb [3:0], 4'h0 });
|
|
crg_timecmp [1] <= new_timecmp;
|
|
|
|
if (cfg_verbosity > 1) begin
|
|
$display (" Writing MTIMECMP");
|
|
$display (" old MTIMECMP = 0x%0h", old_timecmp);
|
|
$display (" new MTIMECMP = 0x%0h", new_timecmp);
|
|
$display (" cur MTIME = 0x%0h", crg_time [1]);
|
|
$display (" new MTIMECMP - MTIME = 0x%0h", new_timecmp - crg_time [1]);
|
|
end
|
|
end
|
|
'h_BFFC: begin
|
|
Bit #(64) old_time = crg_time [1];
|
|
Bit #(64) new_time = fn_update_strobed_bytes (old_time,
|
|
{ wrd.wdata [31:0], 32'h0 },
|
|
{ wrd.wstrb [3:0], 4'h0 });
|
|
crg_time [1] <= new_time;
|
|
|
|
if (cfg_verbosity > 1) begin
|
|
$display (" Writing MTIME");
|
|
$display (" old MTIME = 0x%0h", old_time);
|
|
$display (" new MTIME = 0x%0h", new_time);
|
|
end
|
|
end
|
|
|
|
default: begin
|
|
$display ("%0d: ERROR: Timer.rl_process_wr_req: unrecognized addr", cur_cycle);
|
|
$display (" ", fshow (wra));
|
|
$display (" ", fshow (wrd));
|
|
bresp = AXI4_LITE_SLVERR;
|
|
end
|
|
endcase
|
|
|
|
let wrr = AXI4_Lite_Wr_Resp {bresp: bresp, buser: wra.awuser};
|
|
|
|
slave_xactor.i_wr_resp.enq (wrr);
|
|
|
|
if (cfg_verbosity > 1) begin
|
|
$display (" <= ", fshow (wrr));
|
|
end
|
|
endrule
|
|
|
|
// ================================================================
|
|
// INTERFACE
|
|
|
|
// Reset
|
|
interface server_reset = toGPServer (f_reset_reqs, f_reset_rsps);
|
|
|
|
// set_addr_map should be called after this module's reset
|
|
method Action set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);
|
|
if (addr_base [1:0] != 0)
|
|
$display ("%0d: WARNING: Timer.set_addr_map: addr_base 0x%0h is not 4-Byte-aligned",
|
|
cur_cycle, addr_base);
|
|
|
|
if (addr_lim [1:0] != 0)
|
|
$display ("%0d: WARNING: Timer.set_addr_map: addr_lim 0x%0h is not 4-Byte-aligned",
|
|
cur_cycle, addr_lim);
|
|
|
|
rg_addr_base <= addr_base;
|
|
rg_addr_lim <= addr_lim;
|
|
endmethod
|
|
|
|
// Main Fabric Reqs/Rsps
|
|
interface slave = slave_xactor.axi_side;
|
|
|
|
// External interrupt
|
|
interface Get get_timer_interrupt_req;
|
|
method ActionValue#(Bool) get();
|
|
let x <- toGet (f_timer_interrupt_req).get;
|
|
if (cfg_verbosity > 1)
|
|
$display ("%0d: Timer: get_timer_interrupt_req: %x", cur_cycle, x);
|
|
return x;
|
|
endmethod
|
|
endinterface
|
|
|
|
// Software interrupt
|
|
interface Get get_sw_interrupt_req;
|
|
method ActionValue#(Bool) get();
|
|
let x <- toGet (f_sw_interrupt_req).get;
|
|
if (cfg_verbosity > 1)
|
|
$display ("%0d: Timer: get_sw_interrupt_req: %x", cur_cycle, x);
|
|
return x;
|
|
endmethod
|
|
endinterface
|
|
endmodule
|
|
|
|
// ================================================================
|
|
|
|
endpackage
|