Files
Toooba/src_Core/CPU/MMIOPlatform.bsv
Jonathan Woodruff b830f4bf41 Resolve some issues to build on mac.
This includes renaming Fifo.bsv to Fifos.bsv to account for a case insensitive file system which confuses this library with FIFO.bsv.
Also this includes an update of the verilator flags that are needed for modern verilator.
Finally, some verilator flag changes for building with LLVM.
2020-03-19 19:21:59 +00:00

1090 lines
37 KiB
Plaintext

// Copyright (c) 2018 Massachusetts Institute of Technology
// Portions (c) 2019-2020 Bluespec, Inc.
//
// 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 :: *;
// import BRAMCore::*;
// ----------------
// 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
// ================================================================
// Update relevant bytes of store-value.
// 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
// - an amo result (relevant bytes are in lower-order bits)
// - original store-val
// result:
// - store-val with relevant byte(s) updated
function Bit #(64) fn_update_bytes (Bit #(2) sz, Bit #(64) byte_addr, Bit #(64) value, Bit #(64) st_val);
Bit #(64) result = 0;
Bit #(3) addr_lsbs = byte_addr [2:0];
case (sz)
sz_B: case (addr_lsbs)
'h0: result = { st_val [63:8], value [7:0] };
'h1: result = { st_val [63:16], value [7:0], st_val [7:0] };
'h2: result = { st_val [63:24], value [7:0], st_val [15:0] };
'h3: result = { st_val [63:32], value [7:0], st_val [23:0] };
'h4: result = { st_val [63:40], value [7:0], st_val [31:0] };
'h5: result = { st_val [63:48], value [7:0], st_val [39:0] };
'h6: result = { st_val [63:56], value [7:0], st_val [47:0] };
'h7: result = { value [7:0], st_val [55:0] };
endcase
sz_H: case (addr_lsbs)
'h0: result = { st_val [63:16], value [15:0] };
'h2: result = { st_val [63:32], value [15:0], st_val [15:0] };
'h4: result = { st_val [63:48], value [15:0], st_val [31:0] };
'h6: result = { value [15:0], st_val [47:0] };
endcase
sz_W: case (addr_lsbs)
'h0: result = { st_val [63:32], value [31:0] };
'h4: result = { value [31:0], st_val [31:0] };
endcase
sz_D: case (addr_lsbs) // D
'h0: result = st_val;
endcase
endcase
return result;
endfunction
// ================================================================
// AMO op function
// Extracts the relevant bytes from ld_val and st_val,
// Performs the AMO op,
// Updates the relevant bytes of st_val.
function Bit #(64) fn_amo_op (Bit #(2) sz, // encodes data size (.W or .D)
AmoFunc amofunc, // encodes the AMO op
Bit #(64) addr, // lsbs indicate which 32b W in 64b D (.W)
Bit #(64) ld_val, // 64b value loaded from mem
Bit #(64) st_val); // 64b value from CPU reg Rs2
// Extract relevant bytes of ld_val and st_val
Bit #(64) w1 = fn_extract_and_extend_bytes (sz, addr, ld_val);
Bit #(64) w2 = fn_extract_and_extend_bytes (sz, addr, st_val);
// Do AMO op
Int #(64) i1 = unpack (w1); // Signed, for signed ops
Int #(64) i2 = unpack (w2); // Signed, for signed ops
if (sz == 2'b10) begin
// 32-bit word
w1 = zeroExtend (w1 [31:0]);
w2 = zeroExtend (w2 [31:0]);
i1 = unpack (signExtend (w1 [31:0]));
i2 = unpack (signExtend (w2 [31:0]));
end
Bit #(64) op_result = ?;
case (amofunc)
Swap: op_result = w2;
Add: op_result = pack (i1 + i2);
Xor: op_result = w1 ^ w2;
And: op_result = w1 & w2;
Or: op_result = w1 | w2;
Minu: op_result = ((w1 < w2) ? w1 : w2);
Maxu: op_result = ((w1 > w2) ? w1 : w2);
Min: op_result = ((i1 < i2) ? w1 : w2);
Max: op_result = ((i1 > i2) ? w1 : w2);
endcase
// Update relevant bytes of st_val
return fn_update_bytes (sz, addr, op_result, st_val);
endfunction: fn_amo_op
// ================================================================
// 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;
} 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(Init);
// 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 #(ByteEn) reqBE <- mkRegU;
Reg #(Bit #(2)) reqSz <- mkRegU;
Reg #(Data) reqData <- mkRegU;
// For inst fetch, we need more bookkeepings
// offset of the requested inst within a Data
Reg#(DataInstOffset) instSel <- mkRegU;
// the current superscaler way being fetched
Reg#(SupWaySel) fetchingWay <- mkRegU;
// the already fetched insts
Vector#(TSub#(SupSize, 1),
Reg#(Instruction)) 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#(Data) amoResp <- 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(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);
// 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
// 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 the lower 32 bits of 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: truncate(reqData)
});
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: truncate(reqData)
});
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(reqData[0])
});
end
if(upper_en) begin
cores[upper_core].pRq.enq(MMIOPRq {
target: MSIP,
func: reqFunc,
data: zeroExtend(reqData[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: {upper_data, lower_data}
}));
if(verbosity > 0) begin
$display("[Platform - msip done] lower %x, upper %x",
lower_data, upper_data);
end
endrule
function Data getWriteData(Data orig);
if(reqFunc matches tagged Amo .amoFunc) begin
// amo
Bool doubleWord = reqBE[4] && reqBE[0];
Bool upper32 = reqBE[4] && !reqBE[0];
let amoInst = AmoInst {
func: amoFunc,
doubleWord: doubleWord,
aq: False,
rl: False
};
return amoExec(amoInst, orig, reqData, upper32);
end
else begin
// normal store
Vector#(NumBytes, Bit#(8)) data = unpack(orig);
Vector#(NumBytes, Bit#(8)) wrVec = unpack(reqData);
for(Integer i = 0; i < valueof(NumBytes); i = i+1) begin
if(reqBE[i]) begin
data[i] = wrVec[i];
end
end
return pack(data);
end
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
);
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: oldMTimeCmp
}));
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 = getAmoResp(oldMTimeCmp);
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);
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: mtime
}));
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 = getAmoResp(mtime);
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 = toHostQ.first;
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 = fromHostQ.first;
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};
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 };
let req = MMIOCRq {addr:addr, func:tagged Ld, byteEn:?, data:?};
mmio_fabric_adapter_core_side.request.put (req);
state <= WaitResp;
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 (! dprs.valid) begin
// Access fault
let prs = tagged DataAccess dprs;
cores[reqCore].pRs.enq (prs);
state <= SelectReq;
end
else begin
// Do the AMO op on the loaded value and the store value
let ld_val = dprs.data;
let new_st_val = fn_amo_op (reqSz, reqAmofunc, addr, ld_val, reqData);
// Write back new st_val to fabric
let req = MMIOCRq {addr:addr, func:tagged St, byteEn:reqBE, data:new_st_val};
mmio_fabric_adapter_core_side.request.put (req);
let prs = tagged DataAccess (MMIODataPRs { valid: True, data: ld_val });
cores[reqCore].pRs.enq (prs);
state <= SelectReq;
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 };
let req = MMIOCRq {addr:addr1, func: tagged Ld, byteEn: ?, data: ? };
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 #(SupSize, Maybe #(Instruction)) resp = replicate (Invalid);
for(Integer i = 0; i < valueof (SupSize); i = i+1) begin
if (fromInteger (i) < fetchingWay)
resp [i] = Valid (fetchedInsts [i]);
else if (fromInteger (i) == fetchingWay)
resp [i] = tagged Invalid;
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
let data = dprs.data;
SupWaySel maxWay = 0;
if(reqFunc matches tagged Inst .w) begin
maxWay = w;
end
// View Data as a vector of instructions
Vector#(DataSzInst, Instruction) instVec = unpack(data);
// extract inst from resp data
Instruction 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#(SupSize, Maybe#(Instruction)) resp = replicate(Invalid);
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
if(fromInteger(i) < fetchingWay) begin
resp[i] = Valid (fetchedInsts[i]);
end
else if(fromInteger(i) == fetchingWay) begin
resp[i] = Valid (inst);
end
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 (instSel == maxBound ? addr + 8 : addr);
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) if(state == Init);
toHostAddr <= getDataAlignedAddr(toHost);
fromHostAddr <= getDataAlignedAddr(fromHost);
state <= SelectReq;
endmethod
method ActionValue#(Data) to_host;
toHostQ.deq;
return toHostQ.first;
endmethod
method Action from_host(Data x);
fromHostQ.enq(x);
endmethod
endmodule