// 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