Files
Toooba/src_Testbench/SoC/Timer.bsv
2019-03-26 14:49:40 -04:00

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