1048 lines
39 KiB
Plaintext
1048 lines
39 KiB
Plaintext
// Copyright (c) 2018 Massachusetts Institute of Technology
|
|
// Portions (c) 2019-2020 Bluespec, Inc.
|
|
//
|
|
//-
|
|
// RVFI_DII + CHERI modifications:
|
|
// Copyright (c) 2020 Jessica Clarke
|
|
// Copyright (c) 2020 Alexandre Joannou
|
|
// Copyright (c) 2020 Peter Rugg
|
|
// Copyright (c) 2020 Jonathan Woodruff
|
|
// All rights reserved.
|
|
//
|
|
// This software was developed by SRI International and the University of
|
|
// Cambridge Computer Laboratory (Department of Computer Science and
|
|
// Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
|
|
// DARPA SSITH research programme.
|
|
//
|
|
// This work was supported by NCSC programme grant 4212611/RFA 15971 ("SafeBet").
|
|
//-
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person
|
|
// obtaining a copy of this software and associated documentation
|
|
// files (the "Software"), to deal in the Software without
|
|
// restriction, including without limitation the rights to use, copy,
|
|
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
// of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
// This file is adapted from: MIT-riscy/riscy-OOO/procs/lib/MMIOPlatform.bsv
|
|
// Modifications to fit into Bluespec's RISC-V execution environments.
|
|
|
|
// ================================================================
|
|
// BSV lib imports
|
|
|
|
import Vector::*;
|
|
import GetPut::*;
|
|
import ClientServer::*;
|
|
import Connectable::*;
|
|
import FIFOF :: *;
|
|
|
|
// ----------------
|
|
// BSV additional libs
|
|
|
|
import GetPut_Aux :: *;
|
|
|
|
// ================================================================
|
|
// Project imports
|
|
|
|
// ----------------
|
|
// From MIT RISCY-OOO
|
|
|
|
import Fifos::*;
|
|
import Types::*;
|
|
import ProcTypes::*;
|
|
import CCTypes::*;
|
|
import MMIOAddrs::*;
|
|
import MMIOCore::*;
|
|
import CacheUtils::*;
|
|
import Amo::*;
|
|
|
|
// ----------------
|
|
// From McStriiv
|
|
|
|
import MMIO_AXI4_Adapter :: *;
|
|
|
|
// ================================================================
|
|
// Extract bytes from raw word read from near-mem.
|
|
// The bytes of interest are offset according to LSBs of addr.
|
|
// Arguments:
|
|
// - a RISC-V LD/ST size (encoding B, H, W, or D)
|
|
// - a byte-address
|
|
// - a load-word (loaded from cache/mem)
|
|
// result:
|
|
// - word with correct byte(s) shifted into LSBs and properly extended
|
|
|
|
Bit #(2) sz_B = 2'b00;
|
|
Bit #(2) sz_H = 2'b01;
|
|
Bit #(2) sz_W = 2'b10;
|
|
Bit #(2) sz_D = 2'b11;
|
|
|
|
function Bit #(64) fn_extract_and_extend_bytes (Bit #(2) sz, Bit #(64) byte_addr, Bit #(64) word64);
|
|
Bit #(64) result = 0;
|
|
Bit #(3) addr_lsbs = byte_addr [2:0];
|
|
|
|
case (sz)
|
|
sz_B: case (addr_lsbs)
|
|
'h0: result = zeroExtend (word64 [ 7: 0]);
|
|
'h1: result = zeroExtend (word64 [15: 8]);
|
|
'h2: result = zeroExtend (word64 [23:16]);
|
|
'h3: result = zeroExtend (word64 [31:24]);
|
|
'h4: result = zeroExtend (word64 [39:32]);
|
|
'h5: result = zeroExtend (word64 [47:40]);
|
|
'h6: result = zeroExtend (word64 [55:48]);
|
|
'h7: result = zeroExtend (word64 [63:56]);
|
|
endcase
|
|
|
|
sz_H: case (addr_lsbs)
|
|
'h0: result = zeroExtend (word64 [15: 0]);
|
|
'h2: result = zeroExtend (word64 [31:16]);
|
|
'h4: result = zeroExtend (word64 [47:32]);
|
|
'h6: result = zeroExtend (word64 [63:48]);
|
|
endcase
|
|
|
|
sz_W: case (addr_lsbs)
|
|
'h0: result = zeroExtend (word64 [31: 0]);
|
|
'h4: result = zeroExtend (word64 [63:32]);
|
|
endcase
|
|
|
|
sz_D: case (addr_lsbs) // D
|
|
'h0: result = word64;
|
|
endcase
|
|
endcase
|
|
return result;
|
|
endfunction
|
|
|
|
// ================================================================
|
|
// MMIO logic at platform (MMIOPlatform)
|
|
// XXX Currently all MMIO requests and posts of timer interrupts are handled
|
|
// one by one in a blocking manner. This is extremely conservative. Hopefully
|
|
// this may help avoid some kernel-level problems.
|
|
|
|
interface MMIOPlatform;
|
|
method Action start(Addr toHost, Addr fromHost);
|
|
method ActionValue#(Data) to_host;
|
|
method Action from_host(Data x);
|
|
endinterface
|
|
|
|
typedef enum {
|
|
Init,
|
|
SelectReq,
|
|
ProcessReq,
|
|
WaitResp
|
|
} MMIOPlatformState deriving(Bits, Eq, FShow);
|
|
|
|
// MMIO device/reg targed by the core request together with offset within
|
|
// reg/device
|
|
typedef union tagged {
|
|
void Invalid; // invalid req target
|
|
void TimerInterrupt; // auto-generated timer interrupt
|
|
MSIPDataAlignedOffset MSIP;
|
|
MTimCmpDataAlignedOffset MTimeCmp;
|
|
void MTime;
|
|
void ToHost;
|
|
void FromHost;
|
|
Addr MMIO_Fabric_Adapter;
|
|
void LoadTags;
|
|
} MMIOPlatformReq deriving(Bits, Eq, FShow);
|
|
|
|
module mkMMIOPlatform #(Vector#(CoreNum, MMIOCoreToPlatform) cores,
|
|
Server #(MMIOCRq, MMIODataPRs) mmio_fabric_adapter_core_side)
|
|
(MMIOPlatform)
|
|
|
|
provisos (Bits #(Data, 64)); // this module assumes Data is 64-bit wide
|
|
|
|
Integer verbosity = 0;
|
|
|
|
// mtimecmp
|
|
Vector#(CoreNum, Reg#(Data)) mtimecmp <- replicateM(mkReg(0));
|
|
// mtime
|
|
Reg#(Data) mtime <- mkReg(0);
|
|
// HTIF mem mapped addrs
|
|
Fifo#(1, Data) toHostQ <- mkCFFifo;
|
|
Fifo#(1, Data) fromHostQ <- mkCFFifo;
|
|
Reg#(DataAlignedAddr) toHostAddr <- mkReg(0);
|
|
Reg#(DataAlignedAddr) fromHostAddr <- mkReg(0);
|
|
|
|
// state machine
|
|
Reg#(MMIOPlatformState) state <- mkReg (SelectReq);
|
|
|
|
// current req (valid when state != Init && state != SelectReq
|
|
Reg #(MMIOPlatformReq) curReq <- mkRegU;
|
|
Reg #(CoreId) reqCore <- mkRegU;
|
|
Reg #(MMIOFunc) reqFunc <- mkRegU;
|
|
Reg #(AmoFunc) reqAmofunc <- mkRegU;
|
|
Reg #(MemDataByteEn) reqBE <- mkRegU;
|
|
Reg #(Bit #(2)) reqSz <- mkRegU;
|
|
Reg #(MemTaggedData) reqData <- mkRegU;
|
|
|
|
// For inst fetch, we need more bookkeepings
|
|
// offset of the requested inst within a Data
|
|
Reg#(MemDataInstOffset) instSel <- mkRegU;
|
|
// the current superscaler way being fetched
|
|
Reg#(SupWayX2Sel) fetchingWay <- mkRegU;
|
|
// the already fetched insts
|
|
Vector#(TSub#(SupSizeX2, 1),
|
|
Reg#(Instruction16)) fetchedInsts <- replicateM(mkRegU);
|
|
|
|
// we need to wait for resp from cores when we need to change MTIP
|
|
Reg#(Vector#(CoreNum, Bool)) waitMTIPCRs <- mkRegU;
|
|
|
|
// for MSIP access: lower bits and upper bits of requested memory location
|
|
// correspond to two cores. We need to wait resp from these two cores.
|
|
Reg#(Maybe#(CoreId)) waitLowerMSIPCRs <- mkRegU;
|
|
Reg#(Maybe#(CoreId)) waitUpperMSIPCRs <- mkRegU;
|
|
|
|
// in case of AMO on mtime and mtimecmp, resp may be sent after waiting for
|
|
// CRs, we record the AMO resp at processing time
|
|
Reg#(MemTaggedData) amoResp <- mkRegU;
|
|
|
|
// For AMOs to the fabric, we end up with read and write responses, and need
|
|
// to discard the latter. This tracks which of the two we're waiting for.
|
|
Reg#(Bool) amoWaitWriteResp <- mkRegU;
|
|
|
|
// we increment mtime periodically
|
|
Reg#(Bit#(TLog#(CyclesPerTimeInc))) cycle <- mkReg(0);
|
|
|
|
// To avoid posting timer interrupt repeatedly, we keep a copy of MTIP
|
|
// here. Since each core cannot write MTIP by CSRXXX inst, the only way to
|
|
// change MTIP is through here.
|
|
// We initialize to True to avoid an timer interrupt at start of time.
|
|
Vector#(CoreNum, Reg#(Bool)) mtip <- replicateM(mkReg(True));
|
|
|
|
// pass mtime to each core
|
|
rule propagateTime(state != Init);
|
|
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
|
cores[i].setTime(mtime);
|
|
end
|
|
endrule
|
|
|
|
rule incCycle(
|
|
state != Init &&
|
|
cycle < fromInteger(valueof(CyclesPerTimeInc) - 1)
|
|
);
|
|
cycle <= cycle + 1;
|
|
endrule
|
|
|
|
// we don't increment mtime when processing a req
|
|
rule incTime(
|
|
state == SelectReq &&
|
|
cycle >= fromInteger(valueof(CyclesPerTimeInc) - 1)
|
|
);
|
|
cycle <= 0;
|
|
mtime <= mtime + fromInteger(valueof(TicksPerTimeInc));
|
|
endrule
|
|
|
|
// since we only process 1 MMIO req or timer interrupt at a time, we can
|
|
// enq/deq all FIFOs in one rule
|
|
|
|
(* preempts = "incTime, selectReq" *)
|
|
rule selectReq(state == SelectReq);
|
|
// check for timer interrupt
|
|
Vector#(CoreNum, Bool) needTimerInt = replicate(False);
|
|
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
|
if(!mtip[i] && mtimecmp[i] <= mtime) begin
|
|
cores[i].pRq.enq(MMIOPRq {
|
|
target: MTIP,
|
|
func: St,
|
|
data: 1
|
|
});
|
|
mtip[i] <= True;
|
|
needTimerInt[i] = True;
|
|
end
|
|
end
|
|
if(needTimerInt != replicate(False)) begin
|
|
state <= WaitResp;
|
|
curReq <= TimerInterrupt;
|
|
waitMTIPCRs <= needTimerInt;
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - SelectReq] timer interrupt",
|
|
", mtime %x", mtime,
|
|
", mtimcmp ", fshow(readVReg(mtimecmp)),
|
|
", old mtip ", fshow(readVReg(mtip)),
|
|
", new interrupts ", fshow(needTimerInt));
|
|
end
|
|
end
|
|
else begin
|
|
// now check for MMIO req from core
|
|
function Bool hasReq(Integer i) = cores[i].cRq.notEmpty;
|
|
Vector#(CoreNum, Integer) idxVec = genVector;
|
|
if(find(hasReq, idxVec) matches tagged Valid .i) begin
|
|
cores[i].cRq.deq;
|
|
MMIOCRq req = cores[i].cRq.first;
|
|
// record req
|
|
reqCore <= fromInteger(i);
|
|
reqFunc <= req.func;
|
|
reqAmofunc <= case (req.func) matches
|
|
tagged Amo .f : f;
|
|
default: None;
|
|
endcase;
|
|
reqBE <= req.byteEn;
|
|
reqData <= req.data;
|
|
reqSz <= sz_D; // TODO: may be sz_H, sz_B or sz_W
|
|
// set up bookkeepings in case of inst fetch (other
|
|
// bookkeepings are set at processing time)
|
|
instSel <= truncate(req.addr >> valueof(LgInstSzBytes));
|
|
fetchingWay <= 0;
|
|
// find out which MMIO reg/device is being requested
|
|
DataAlignedAddr addr = getDataAlignedAddr(req.addr);
|
|
MMIOPlatformReq newReq = Invalid;
|
|
|
|
if(req.loadTags) begin
|
|
newReq = LoadTags;
|
|
end
|
|
else if(addr >= msipBaseAddr && addr < msipBoundAddr) begin
|
|
newReq = MSIP (truncate(addr - msipBaseAddr));
|
|
end
|
|
else if(addr >= mtimecmpBaseAddr &&
|
|
addr < mtimecmpBoundAddr)
|
|
begin
|
|
newReq = MTimeCmp (truncate(addr - mtimecmpBaseAddr));
|
|
end
|
|
else if(addr == mtimeBaseAddr) begin
|
|
// assume mtime is of size Data
|
|
newReq = MTime;
|
|
end
|
|
else if(addr == toHostAddr) begin
|
|
// assume tohost is of size Data
|
|
newReq = ToHost;
|
|
end
|
|
else if(addr == fromHostAddr) begin
|
|
// assume fromhost is of size Data
|
|
newReq = FromHost;
|
|
end
|
|
else begin // Send all remaining reqs to the fabric adapter, as is
|
|
newReq = MMIO_Fabric_Adapter (req.addr);
|
|
end
|
|
curReq <= newReq;
|
|
|
|
// process valid req
|
|
state <= ProcessReq;
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - SelectReq] core %d, req ", i, fshow(req));
|
|
$display(" req type ", fshow(newReq));
|
|
end
|
|
end
|
|
end
|
|
endrule
|
|
|
|
// handle new timer interrupt: wait for writes on MTIP to be done
|
|
rule waitTimerInterruptDone(state == WaitResp && curReq == TimerInterrupt);
|
|
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
|
if(waitMTIPCRs[i]) begin
|
|
cores[i].cRs.deq;
|
|
end
|
|
end
|
|
state <= SelectReq;
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - Done] timer interrupt",
|
|
", mtip ", fshow(readVReg(mtip)),
|
|
", waitCRs ", fshow(waitMTIPCRs));
|
|
end
|
|
endrule
|
|
|
|
// Classify the request
|
|
Bool isInstFetch = (reqFunc matches tagged Inst .x ? True : False);
|
|
Bool isAmo = (reqFunc matches tagged Amo .amofunc ? True : False);
|
|
Bool isLd = (reqFunc matches tagged Ld ? True : False);
|
|
Bool isSt = (reqFunc matches tagged St ? True : False);
|
|
|
|
rule processLoadTags(
|
|
// LoadTags to MMIO, fault
|
|
curReq == LoadTags &&& state == ProcessReq
|
|
);
|
|
state <= SelectReq;
|
|
cores[reqCore].pRs.enq(DataAccess (MMIODataPRs {
|
|
valid: False, data: ?
|
|
}));
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process LoadTags] attempted LoadTags from MMIO, which is illegal");
|
|
end
|
|
endrule
|
|
|
|
// handle MSIP access
|
|
rule processMSIP(
|
|
curReq matches tagged MSIP .offset &&& state == ProcessReq
|
|
);
|
|
// core corresponding to lower bits of requested Data
|
|
CoreId lower_core = truncate({offset, 1'b0});
|
|
Bool lower_en = reqBE[0];
|
|
// core corresponding to upper bits of requested Data. Need to check if
|
|
// this core truly exists
|
|
CoreId upper_core = truncate({offset, 1'b1});
|
|
Bool upper_valid = {offset, 1'b1} <= fromInteger(valueof(CoreNum) - 1);
|
|
Bool upper_en = reqBE[4];
|
|
|
|
if(isInstFetch) begin
|
|
state <= SelectReq;
|
|
cores[reqCore].pRs.enq(InstFetch (replicate(Invalid)));
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process msip] cannot do inst fetch");
|
|
end
|
|
end
|
|
else if(upper_en && !upper_valid) begin
|
|
// access invalid core's MSIP, fault
|
|
state <= SelectReq;
|
|
cores[reqCore].pRs.enq(DataAccess (MMIODataPRs {
|
|
valid: False, data: ?
|
|
}));
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process msip] access invalid core");
|
|
end
|
|
end
|
|
else if(reqFunc matches tagged Amo .amoFunc) begin
|
|
Tuple2#(Bit#(32), Bit#(4)) amo_req = fromMemTaggedDataSelect(reqData, pack(reqBE));
|
|
match {.amo_req_data, .amo_req_be} = amo_req;
|
|
// AMO req: should only access MSIP of one core. Thus, we always
|
|
// treat the accessed core as the lower core to save the shift (AMO
|
|
// resp is different from load that valid data is already shifted
|
|
// to LSBs). Besides, we only use 32 bits reqData.
|
|
if(lower_en && upper_en) begin
|
|
state <= SelectReq;
|
|
cores[reqCore].pRs.enq(DataAccess (MMIODataPRs {
|
|
valid: False, data: ?
|
|
}));
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process msip] ",
|
|
"AMO cannot access 2 cores");
|
|
end
|
|
end
|
|
else if(lower_en) begin
|
|
cores[lower_core].pRq.enq(MMIOPRq {
|
|
target: MSIP,
|
|
func: reqFunc,
|
|
data: amo_req_data
|
|
});
|
|
waitLowerMSIPCRs <= Valid (lower_core);
|
|
waitUpperMSIPCRs <= Invalid;
|
|
state <= WaitResp;
|
|
end
|
|
else if(upper_en) begin
|
|
cores[upper_core].pRq.enq(MMIOPRq {
|
|
target: MSIP,
|
|
func: reqFunc,
|
|
data: amo_req_data
|
|
});
|
|
waitLowerMSIPCRs <= Valid (upper_core);
|
|
waitUpperMSIPCRs <= Invalid;
|
|
state <= WaitResp;
|
|
end
|
|
else begin
|
|
// AMO access nothing: fault
|
|
state <= SelectReq;
|
|
cores[reqCore].pRs.enq(DataAccess (MMIODataPRs {
|
|
valid: False, data: ?
|
|
}));
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process msip] access nothing");
|
|
end
|
|
end
|
|
end
|
|
else begin
|
|
// normal load and store
|
|
if(lower_en) begin
|
|
cores[lower_core].pRq.enq(MMIOPRq {
|
|
target: MSIP,
|
|
func: reqFunc,
|
|
data: zeroExtend(pack(reqData.data)[0])
|
|
});
|
|
end
|
|
if(upper_en) begin
|
|
cores[upper_core].pRq.enq(MMIOPRq {
|
|
target: MSIP,
|
|
func: reqFunc,
|
|
data: zeroExtend(pack(reqData.data)[32])
|
|
});
|
|
end
|
|
state <= WaitResp;
|
|
waitLowerMSIPCRs <= lower_en ? Valid (lower_core) : Invalid;
|
|
waitUpperMSIPCRs <= upper_en ? Valid (upper_core) : Invalid;
|
|
end
|
|
endrule
|
|
|
|
rule waitMSIPDone(
|
|
curReq matches tagged MSIP .offset &&& state == WaitResp
|
|
);
|
|
Bit#(32) lower_data = 0;
|
|
Bit#(32) upper_data = 0;
|
|
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
|
if (waitLowerMSIPCRs matches tagged Valid .c &&&
|
|
c == fromInteger(i)) begin
|
|
cores[i].cRs.deq;
|
|
lower_data = zeroExtend(cores[i].cRs.first.data);
|
|
end
|
|
else if(waitUpperMSIPCRs matches tagged Valid .c &&&
|
|
c == fromInteger(i)) begin
|
|
cores[i].cRs.deq;
|
|
upper_data = zeroExtend(cores[i].cRs.first.data);
|
|
end
|
|
end
|
|
state <= SelectReq;
|
|
cores[reqCore].pRs.enq(DataAccess (MMIODataPRs {
|
|
valid: True,
|
|
// for AMO, resp data should be signExtend(lower_data). However,
|
|
// lower_data is just 1 or 0, and upper_data is always 0, so we
|
|
// don't need to do signExtend.
|
|
data: toMemTaggedDataSelect({upper_data, lower_data}, {msipBoundAddr,0})
|
|
}));
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - msip done] lower %x, upper %x",
|
|
lower_data, upper_data);
|
|
end
|
|
endrule
|
|
|
|
function Data getWriteData(Data orig);
|
|
Tuple2#(Data, Bit#(DataBytes)) wr = fromMemTaggedDataSelect(reqData, pack(reqBE));
|
|
match {.wr_data, .wr_be} = wr;
|
|
if(reqFunc matches tagged Amo .amoFunc) begin
|
|
// amo
|
|
let amoInst = AmoInst {
|
|
func: amoFunc,
|
|
width: wr_be[4]==1 && wr_be[0]==1 ? DWord : Word,
|
|
aq: False,
|
|
rl: False
|
|
};
|
|
let res = amoExec(amoInst, {0, pack(wr_be[4]==1 && wr_be[0]!=1)},
|
|
toMemTaggedData(orig), toMemTaggedData(wr_data));
|
|
|
|
return res.data[0];
|
|
end
|
|
else return mergeDataBE(orig, wr_data, wr_be);
|
|
endfunction
|
|
|
|
function Data getAmoResp(Data orig);
|
|
if(reqBE[4] && reqBE[0]) begin
|
|
// double word
|
|
return orig;
|
|
end
|
|
else if(reqBE[4]) begin
|
|
// upper 32 bit
|
|
return signExtend(orig[63:32]);
|
|
end
|
|
else begin
|
|
// lower 32 bit
|
|
return signExtend(orig[31:0]);
|
|
end
|
|
endfunction
|
|
|
|
// handle mtimecmp access
|
|
rule processMTimeCmp(
|
|
curReq matches tagged MTimeCmp .offset &&& state == ProcessReq
|
|
);
|
|
Addr addr = {mtimecmpBaseAddr,0};
|
|
if(isInstFetch) begin
|
|
state <= SelectReq;
|
|
cores[reqCore].pRs.enq(InstFetch (replicate(Invalid)));
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process mtimecmp] cannot do inst fetch");
|
|
end
|
|
end
|
|
else if(offset > fromInteger(valueof(CoreNum) - 1)) begin
|
|
// access invalid core's mtimecmp, fault
|
|
cores[reqCore].pRs.enq(DataAccess (MMIODataPRs {
|
|
valid: False, data: ?
|
|
}));
|
|
state <= SelectReq;
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process mtimecmp] access fault");
|
|
end
|
|
end
|
|
else begin
|
|
let oldMTimeCmp = mtimecmp[offset];
|
|
if(reqFunc == Ld) begin
|
|
cores[reqCore].pRs.enq(DataAccess (MMIODataPRs {
|
|
valid: True,
|
|
data: toMemTaggedDataSelect(oldMTimeCmp, addr)
|
|
}));
|
|
state <= SelectReq;
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process mtimecmp] read done, data %x",
|
|
oldMTimeCmp);
|
|
end
|
|
end
|
|
else begin
|
|
// do updates for store or AMO
|
|
let newData = getWriteData(oldMTimeCmp);
|
|
mtimecmp[offset] <= newData;
|
|
// get and record amo resp
|
|
let respData = toMemTaggedDataSelect(getAmoResp(oldMTimeCmp), addr);
|
|
amoResp <= respData;
|
|
// check changes to MTIP
|
|
if(newData <= mtime && !mtip[offset]) begin
|
|
// need to post new timer interrupt
|
|
mtip[offset] <= True;
|
|
cores[offset].pRq.enq(MMIOPRq {
|
|
target: MTIP,
|
|
func: St,
|
|
data: 1
|
|
});
|
|
state <= WaitResp;
|
|
end
|
|
else if(newData > mtime && mtip[offset]) begin
|
|
// need to clear timer interrupt
|
|
mtip[offset] <= False;
|
|
cores[offset].pRq.enq(MMIOPRq {
|
|
target: MTIP,
|
|
func: St,
|
|
data: 0
|
|
});
|
|
state <= WaitResp;
|
|
end
|
|
else begin
|
|
// nothing happens to mtip, just finish this req
|
|
cores[reqCore].pRs.enq(DataAccess (MMIODataPRs {
|
|
valid: True,
|
|
// store doesn't need resp data, just fill in AMO resp
|
|
data: respData
|
|
}));
|
|
state <= SelectReq;
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process mtimecmp] ",
|
|
"no change to mtip ", fshow(readVReg(mtip)),
|
|
", mtime %x", mtime,
|
|
", old mtimecmp ", fshow(readVReg(mtimecmp)),
|
|
", new mtimecmp[%d] %x", offset, newData);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
endrule
|
|
|
|
rule waitMTimeCmpDone(
|
|
curReq matches tagged MTimeCmp .offset &&& state == WaitResp
|
|
);
|
|
cores[offset].cRs.deq;
|
|
cores[reqCore].pRs.enq(DataAccess (MMIODataPRs {
|
|
valid: True,
|
|
// store doesn't need resp data, just fill in AMO resp. We cannot
|
|
// recompute AMO resp now, because mtimecmp has changed
|
|
data: amoResp
|
|
}));
|
|
state <= SelectReq;
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - mtimecmp done]",
|
|
", mtime %x", mtime,
|
|
", mtimecmp ", fshow(readVReg(mtimecmp)),
|
|
", mtip ", fshow(readVReg(mtip)));
|
|
end
|
|
endrule
|
|
|
|
// handle mtime access
|
|
rule processMTime(state == ProcessReq && curReq == MTime);
|
|
Addr addr = {mtimeBaseAddr,0};
|
|
if(isInstFetch) begin
|
|
state <= SelectReq;
|
|
cores[reqCore].pRs.enq(InstFetch (replicate(Invalid)));
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process mtime] cannot do inst fetch");
|
|
end
|
|
end
|
|
else if(reqFunc == Ld) begin
|
|
cores[reqCore].pRs.enq(DataAccess (MMIODataPRs {
|
|
valid: True, data: toMemTaggedDataSelect(mtime, addr)
|
|
}));
|
|
state <= SelectReq;
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process mtime] read done, data %x",
|
|
mtime);
|
|
end
|
|
end
|
|
else begin
|
|
// do update for store or AMO
|
|
let newData = getWriteData(mtime);
|
|
mtime <= newData;
|
|
// get and record AMO resp
|
|
let respData = toMemTaggedDataSelect(getAmoResp(mtime), addr);
|
|
amoResp <= respData;
|
|
// check change in MTIP
|
|
Vector#(CoreNum, Bool) changeMTIP = replicate(False);
|
|
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
|
if(mtimecmp[i] <= newData && !mtip[i]) begin
|
|
cores[i].pRq.enq(MMIOPRq {
|
|
target: MTIP,
|
|
func: St,
|
|
data: 1
|
|
});
|
|
changeMTIP[i] = True;
|
|
end
|
|
else if(mtimecmp[i] > newData && mtip[i]) begin
|
|
cores[i].pRq.enq(MMIOPRq {
|
|
target: MTIP,
|
|
func: St,
|
|
data: 0
|
|
});
|
|
changeMTIP[i] = True;
|
|
end
|
|
end
|
|
if(changeMTIP != replicate(False)) begin
|
|
waitMTIPCRs <= changeMTIP;
|
|
state <= WaitResp;
|
|
end
|
|
else begin
|
|
cores[reqCore].pRs.enq(DataAccess (MMIODataPRs {
|
|
valid: True,
|
|
data: respData // AMO resp
|
|
}));
|
|
state <= SelectReq;
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process mtime] ",
|
|
"no change to mtip ", fshow(readVReg(mtip)),
|
|
", new mtime %x", newData,
|
|
", mtimecmp ", fshow(readVReg(mtimecmp)));
|
|
end
|
|
end
|
|
end
|
|
endrule
|
|
|
|
rule waitMTimeDone(state == WaitResp && curReq == MTime);
|
|
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
|
if(waitMTIPCRs[i]) begin
|
|
cores[i].cRs.deq;
|
|
end
|
|
end
|
|
cores[reqCore].pRs.enq(DataAccess (MMIODataPRs {
|
|
valid: True,
|
|
data: amoResp // recorded amo resp
|
|
}));
|
|
state <= SelectReq;
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - mtime done]",
|
|
", mtime %x", mtime,
|
|
", mtimecmp ", fshow(readVReg(mtimecmp)),
|
|
", mtip ", fshow(readVReg(mtip)));
|
|
end
|
|
endrule
|
|
|
|
// handle tohost access
|
|
rule processToHost(state == ProcessReq && curReq == ToHost);
|
|
if(isInstFetch) begin
|
|
state <= SelectReq;
|
|
cores[reqCore].pRs.enq(InstFetch (replicate(Invalid)));
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process tohost] cannot do inst fetch");
|
|
end
|
|
end
|
|
else begin
|
|
let resp = MMIODataPRs {valid: False, data: ?};
|
|
if(reqFunc == St) begin
|
|
if(toHostQ.notEmpty) begin
|
|
doAssert(False,
|
|
"Cannot write tohost when toHostQ not empty");
|
|
// this will raise access fault
|
|
end
|
|
else begin
|
|
let data = getWriteData(0);
|
|
if(data != 0) begin // 0 means nothing for tohost
|
|
toHostQ.enq(data);
|
|
end
|
|
resp.valid = True;
|
|
end
|
|
end
|
|
else if(reqFunc == Ld) begin
|
|
resp.valid = True;
|
|
if(toHostQ.notEmpty) begin
|
|
resp.data = toMemTaggedDataSelect(toHostQ.first, {toHostAddr,0});
|
|
end
|
|
else begin
|
|
resp.data = 0;
|
|
end
|
|
end
|
|
else begin
|
|
// amo: access fault
|
|
doAssert(False, "Cannot do AMO on toHost");
|
|
end
|
|
state <= SelectReq;
|
|
cores[reqCore].pRs.enq(DataAccess (resp));
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process tohost] resp ", fshow(resp));
|
|
end
|
|
end
|
|
endrule
|
|
|
|
// handle fromhost access
|
|
rule processFromHost(state == ProcessReq && curReq == FromHost);
|
|
if(isInstFetch) begin
|
|
state <= SelectReq;
|
|
cores[reqCore].pRs.enq(InstFetch (replicate(Invalid)));
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process fromhost] cannot do inst fetch");
|
|
end
|
|
end
|
|
else begin
|
|
let resp = MMIODataPRs {valid: False, data: ?};
|
|
if(reqFunc == St) begin
|
|
if(fromHostQ.notEmpty) begin
|
|
if(getWriteData(fromHostQ.first) == 0) begin
|
|
fromHostQ.deq;
|
|
resp.valid = True;
|
|
end
|
|
else begin
|
|
doAssert(False, "Can only write 0 to fromhost");
|
|
end
|
|
end
|
|
else begin
|
|
if(getWriteData(0) == 0) begin
|
|
resp.valid = True;
|
|
end
|
|
else begin
|
|
doAssert(False, "Can only write 0 to fromhost");
|
|
end
|
|
end
|
|
end
|
|
else if(reqFunc == Ld) begin
|
|
resp.valid = True;
|
|
if(fromHostQ.notEmpty) begin
|
|
resp.data = toMemTaggedDataSelect(fromHostQ.first, {fromHostAddr,0});
|
|
end
|
|
else begin
|
|
resp.data = 0;
|
|
end
|
|
end
|
|
else begin
|
|
// amo: access fault
|
|
doAssert(False, "Cannot do AMO on fromHost");
|
|
end
|
|
state <= SelectReq;
|
|
cores[reqCore].pRs.enq(DataAccess (resp));
|
|
if(verbosity > 0) begin
|
|
$display("[Platform - process fromhost] resp ", fshow(resp));
|
|
end
|
|
end
|
|
endrule
|
|
|
|
// ================================================================
|
|
// ================================================================
|
|
// ================================================================
|
|
// All remaining IO (not MTIMECMP, MSIP, fromHost, toHost) goes to the fabric
|
|
// Instruction-fetches are treated specially (collect a superscalar set of instrs)
|
|
|
|
// ================================================================
|
|
// MMIO to Fabric: Load/Store (not Instruction Fetch)
|
|
|
|
// Forward the request as-is to the fabric adapter.
|
|
rule rl_mmio_to_fabric_req (curReq matches tagged MMIO_Fabric_Adapter .addr
|
|
&&& (state == ProcessReq)
|
|
&&& (isLd || isSt));
|
|
let req = MMIOCRq {addr:addr, func:reqFunc, byteEn:reqBE, data:reqData, loadTags:False};
|
|
mmio_fabric_adapter_core_side.request.put (req);
|
|
state <= WaitResp;
|
|
|
|
if (verbosity > 0) begin
|
|
$display ("MMIOPlatform.rl_mmio_to_fabric_req");
|
|
$display (" ", fshow (req));
|
|
end
|
|
endrule
|
|
|
|
// Forward the fabric-adapter's response as-is to the core.
|
|
rule rl_mmio_from_fabric_rsp (curReq matches tagged MMIO_Fabric_Adapter .addr
|
|
&&& (state == WaitResp)
|
|
&&& (isLd || isSt));
|
|
MMIODataPRs dprs <- mmio_fabric_adapter_core_side.response.get;
|
|
let prs = tagged DataAccess dprs;
|
|
cores[reqCore].pRs.enq (prs);
|
|
state <= SelectReq;
|
|
|
|
if (verbosity > 0) begin
|
|
$display ("MMIOPlatform.rl_mmio_from_fabric_rsp");
|
|
$display (" ", fshow (prs));
|
|
end
|
|
endrule
|
|
|
|
// ================================================================
|
|
// MMIO to Fabric: AMO (not Instruction Fetch)
|
|
|
|
rule rl_mmio_to_fabric_amo_req (curReq matches tagged MMIO_Fabric_Adapter .addr
|
|
&&& (state == ProcessReq)
|
|
&&& isAmo);
|
|
// Send a load-request to the fabric adapter.
|
|
// Align addr to 8-byte boundary (FabricData-aligned)
|
|
Addr addr1 = { addr [63:3], 3'b_000 };
|
|
// Byte enables are used in the AXI adapter to determine the size of the req. Set 8 bits (it
|
|
// doesn't matter which) to preserve the behaviour of requesting 8 bytes).
|
|
// TODO: instead specify access size in interface
|
|
let req = MMIOCRq {addr:addr, func:tagged Ld, byteEn:unpack(16'b0000_0000_1111_1111), data:?, loadTags:False};
|
|
mmio_fabric_adapter_core_side.request.put (req);
|
|
state <= WaitResp;
|
|
amoWaitWriteResp <= False;
|
|
|
|
if (verbosity > 0) begin
|
|
$display ("MMIOPlatform.rl_mmio_to_fabric_amo_req: addr 0x%0h", addr);
|
|
$display (" ", fshow (req));
|
|
end
|
|
endrule
|
|
|
|
// Get the Load-response; do the AMO op; send final write back to fabric, and respond to core
|
|
rule rl_mmio_from_fabric_amo_rsp (curReq matches tagged MMIO_Fabric_Adapter .addr
|
|
&&& (state == WaitResp)
|
|
&&& isAmo);
|
|
MMIODataPRs dprs <- mmio_fabric_adapter_core_side.response.get;
|
|
|
|
if (amoWaitWriteResp) begin
|
|
// Discard the write response; we're now ready for another request
|
|
state <= SelectReq;
|
|
end
|
|
else if (! dprs.valid) begin
|
|
// Access fault
|
|
let prs = tagged DataAccess dprs;
|
|
cores[reqCore].pRs.enq (prs);
|
|
state <= SelectReq;
|
|
end
|
|
else begin
|
|
MemTaggedData ld_val = dprs.data;
|
|
// Do the AMO op on the loaded value and the store value
|
|
function Bool pred(Bool b) = b;
|
|
let set_bes = countIf(pred, reqBE);
|
|
let amoInst = AmoInst {
|
|
func: reqAmofunc,
|
|
width: (set_bes > 4) ? DWord : Word,
|
|
aq: False,
|
|
rl: False
|
|
};
|
|
let new_st_val = amoExec(amoInst, addr[3:2],
|
|
ld_val, reqData);
|
|
|
|
// Write back new st_val to fabric
|
|
let req = MMIOCRq {addr:addr, func:tagged St, byteEn:reqBE, data: new_st_val, loadTags: False};
|
|
mmio_fabric_adapter_core_side.request.put (req);
|
|
|
|
let prs = tagged DataAccess (MMIODataPRs { valid: True, data: ld_val });
|
|
cores[reqCore].pRs.enq (prs);
|
|
// Stay in WaitResp but wait to discard the write response
|
|
amoWaitWriteResp <= True;
|
|
|
|
// if (verbosity > 1) begin
|
|
$display ("MMIO_Platform.rl_mmio_from_fabric_amo_rsp: addr 0x%0h, size %0d, amofunc %0d",
|
|
addr, reqSz, reqAmofunc);
|
|
$display (" ld_val 0x%0h op st_val 0x%0h => new_st_val 0x%0h", ld_val, reqData, new_st_val);
|
|
// end
|
|
end
|
|
endrule
|
|
|
|
// ================================================================
|
|
// MMIO to Fabric: Instruction Fetch
|
|
// (This code adapted from MMIOPlatform::processBootRomInst and waitBootRomInst)
|
|
// Loops, collecting and returning a super-scalar-wide set of instructions (0..maxWay).
|
|
// Note: may repeatedly fetch the same Data word as it collects instuctions.
|
|
// Expected to be used only for initial boot ROM, so speed is not critical.
|
|
// TODO: Candidate for future optimization.
|
|
// The original request had func = Inst maxWay
|
|
// instSel: initial instruction index in a Data word: truncate(req.addr >> valueof(LgInstSzBytes))
|
|
// fetchingWay: initial 0
|
|
|
|
rule rl_mmio_to_fabric_ifetch_req (curReq matches tagged MMIO_Fabric_Adapter .addr
|
|
&&& (state == ProcessReq)
|
|
&&& isInstFetch);
|
|
// Note: addr may not be FabricData-aligned; result will be Data that contains addr
|
|
// TODO: currently assumes superscalarity fits in fabric width
|
|
Addr addr1 = { addr [63:3], 3'b_000 };
|
|
// Byte enables are used in the AXI adapter to determine the size of the req. Set 8 bits (it
|
|
// doesn't matter which) to preserve the behaviour of requesting 8 bytes).
|
|
// TODO: instead specify access size in interface
|
|
let req = MMIOCRq {addr:addr1, func: tagged Ld, byteEn: unpack(16'b0000_0000_1111_1111), data: ?, loadTags: False};
|
|
mmio_fabric_adapter_core_side.request.put (req);
|
|
state <= WaitResp;
|
|
|
|
if (verbosity > 0) begin
|
|
$display ("MMIOPlatform.rl_mmio_to_fabric_ifetch_req: addr 0x%0h fetchingWay %0d",
|
|
addr, fetchingWay);
|
|
$display (" ", fshow (req));
|
|
end
|
|
endrule
|
|
|
|
rule rl_mmio_from_fabric_ifetch_rsp (curReq matches tagged MMIO_Fabric_Adapter .addr
|
|
&&& (state == WaitResp)
|
|
&&& isInstFetch);
|
|
MMIODataPRs dprs <- mmio_fabric_adapter_core_side.response.get;
|
|
if (! dprs.valid) begin
|
|
// Access fault
|
|
Vector #(SupSizeX2, Maybe #(Instruction16)) resp = replicate (Invalid);
|
|
for(Integer i = 0; i < valueof (SupSizeX2); i = i+1) begin
|
|
if (fromInteger (i) <= fetchingWay)
|
|
resp [i] = Valid (0);
|
|
end
|
|
cores[reqCore].pRs.enq (tagged InstFetch resp);
|
|
state <= SelectReq;
|
|
|
|
if (verbosity > 0) begin
|
|
$display ("MMIOPlatform.rl_mmio_from_fabric_ifetch_rsp: access fault; final resp to core:");
|
|
$display (" ", fshow (resp));
|
|
end
|
|
end
|
|
|
|
else begin
|
|
// No access fault
|
|
|
|
SupWayX2Sel maxWay = 0;
|
|
if(reqFunc matches tagged Inst .w) begin
|
|
maxWay = w;
|
|
end
|
|
|
|
// View Data as a vector of instructions
|
|
Vector#(MemDataSzInst, Instruction16) instVec = unpack(pack(dprs.data.data));
|
|
// extract inst from resp data
|
|
Instruction16 inst = instVec[instSel];
|
|
// check whether we are done or not
|
|
if (fetchingWay >= maxWay) begin
|
|
// all 0..maxWay insts are fetched; we can resp now
|
|
Vector#(SupSizeX2, Maybe#(Instruction16)) resp = replicate(Invalid);
|
|
for(Integer i = 0; i < valueof(SupSizeX2); i = i+1) begin
|
|
if(fromInteger(i) < fetchingWay)
|
|
resp[i] = Valid (fetchedInsts[i]);
|
|
else if(fromInteger(i) == fetchingWay)
|
|
resp[i] = Valid (inst);
|
|
end
|
|
cores[reqCore].pRs.enq (tagged InstFetch resp);
|
|
state <= SelectReq;
|
|
|
|
if (verbosity > 0) begin
|
|
$display ("MMIOPlatform.rl_mmio_from_fabric_ifetch_rsp: final resp to core:");
|
|
$display (" ", fshow (resp));
|
|
end
|
|
end
|
|
else begin
|
|
// continue to fetch next inst, save current inst, increment offset
|
|
fetchedInsts[fetchingWay] <= inst;
|
|
fetchingWay <= fetchingWay + 1;
|
|
instSel <= instSel + 1;
|
|
curReq <= MMIO_Fabric_Adapter (addr + 2);
|
|
state <= ProcessReq;
|
|
|
|
if (verbosity > 0) begin
|
|
$display ("MMIOPlatform.rl_mmio_from_fabric_ifetch_rsp:");
|
|
$display (" fetchingWay %0d instSel %0d inst 0x%0h", fetchingWay, instSel, inst);
|
|
end
|
|
end
|
|
end
|
|
endrule
|
|
|
|
// ================================================================
|
|
// ================================================================
|
|
// ================================================================
|
|
// INTERFACE
|
|
|
|
method Action start(Addr toHost, Addr fromHost);
|
|
toHostAddr <= getDataAlignedAddr(toHost);
|
|
fromHostAddr <= getDataAlignedAddr(fromHost);
|
|
endmethod
|
|
|
|
method ActionValue#(Data) to_host;
|
|
toHostQ.deq;
|
|
return toHostQ.first;
|
|
endmethod
|
|
|
|
method Action from_host(Data x);
|
|
fromHostQ.enq(x);
|
|
endmethod
|
|
endmodule
|