Files
Toooba/src_Core/CPU/MMIO_AXI4_Adapter.bsv
Jonathan Woodruff 066289f8aa Work in progress.
Wd_Data is moved to 512 bits, and LLC_AXI4_Adapter is updated.
Lots of build errors now; a few have been resolved.
2022-11-10 13:51:12 +00:00

341 lines
13 KiB
Plaintext

// Copyright (c) 2019-2020 Bluespec, Inc.
//
//-
// RVFI_DII + CHERI modifications:
// Copyright (c) 2020 Alexandre Joannou
// 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").
//-
package MMIO_AXI4_Adapter;
// ================================================================
// This is an adapter to connect MIT's RISCY-OOO to an AXI4 fabric in
// Bluespec's Toooba setup. All IO traffic to the fabric flows through
// this. Note: a few IO addresses (e.g., MTIME, MTIMECMP, MSIP,
// TOHOST, FROMHOST are intercepted and handled before they reach this
// adapter).
// ================================================================
// BSV lib imports
import Assert :: *;
import ConfigReg :: *;
import FIFOF :: *;
import GetPut :: *;
import ClientServer :: *;
import Vector :: *;
// ----------------
// BSV additional libs
import GetPut_Aux :: *;
import Cur_Cycle :: *;
import Semi_FIFOF :: *;
import CreditCounter :: *;
// ================================================================
// Project imports
// ----------------
// From MIT RISCY-OOO
import Types :: *;
import ProcTypes :: *;
// ----------------
// From Bluespec Pipes
import AXI4 :: *;
import SourceSink :: *;
import Fabric_Defs :: *;
import SoC_Map :: *;
// ================================================================
interface MMIO_AXI4_Adapter_IFC;
method Action reset;
interface Server #(MMIOCRq, MMIODataPRs) core_side;
// Fabric master interface for IO
interface AXI4_Master #( Wd_CoreW_Bus_MId, Wd_Addr, Wd_Data_Periph
, Wd_AW_User, Wd_W_User, Wd_B_User
, Wd_AR_User, Wd_R_User) mmio_master;
endinterface
// ================================================================
module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
// Verbosity: 0: quiet; 1: transactions
Integer verbosity = 0;
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (fromInteger (verbosity));
// ================================================================
// Requests from and responses to core
FIFOF #(MMIOCRq) f_reqs_from_core <- mkFIFOF;
FIFOF #(MMIODataPRs) f_rsps_to_core <- mkFIFOF;
Reg#(Fabric_Addr) read_req_addr <- mkRegU;
SoC_Map_IFC soc_map <- mkSoC_Map; // for m_is_IO_addr
// ================================================================
// Fabric request/response
let master_shim <- mkAXI4ShimFF;
// For discarding write-responses
CreditCounter_IFC #(4) ctr_wr_rsps_pending <- mkCreditCounter; // Max 15 writes outstanding
// ================================================================
// Handle read requests and responses.
// Don't do a read while a write is outstanding.
// This is just an adapter from MMIOCRq/MMIODataPRs to AXI4
Reg #(Bit #(1)) rg_rd_rsp_beat <- mkReg (0);
Reg #(MemTaggedData) rspData <- mkReg (unpack(0));
// ================================================================
// Convert a request's byte enables to an AXI 4 size, to ensure that
// peripherals don't get confused about how large our access is.
function AXI4_Size byteEnToAxiSize (MemDataByteEn byteEn);
return toAXI4_Size(zeroExtend(pack(countIf(id, byteEn)))).Valid;
endfunction
rule rl_handle_read_req (f_reqs_from_core.first.func matches Ld
&&& (ctr_wr_rsps_pending.value == 0));
let req <- pop (f_reqs_from_core);
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_handle_read_req: Ld request", cur_cycle);
$display (" ", fshow (req));
end
AXI4_Size size = byteEnToAxiSize(req.byteEn);
Bool burst = size > 8;
if (burst) size = 8;
// Technically the following check for legal IO addrs is not
// necessary; the AXI4 fabric should return a DECERR for illegal
// addrs; but not all AXI4 fabrics do the right thing.
if (soc_map.m_is_IO_addr (req.addr, False)) begin
let mem_req_rd_addr = AXI4_ARFlit {arid: fabric_corew_bus_default_mid,
araddr: req.addr,
arlen: (burst) ? 1:0, // burst len = arlen+1
arsize: size,
arburst: fabric_default_burst,
arlock: fabric_default_lock,
arcache: fabric_default_arcache,
arprot: fabric_default_prot,
arqos: fabric_default_qos,
arregion: fabric_default_region,
aruser: fabric_default_aruser};
master_shim.slave.ar.put(mem_req_rd_addr);
read_req_addr <= req.addr;
// Debugging
if (cfg_verbosity > 0) begin
$display (" ", fshow (mem_req_rd_addr));
end
end else begin
let rsp = MMIODataPRs {valid: False,
data: toMemTaggedData(req.addr)}; // For debugging convenience only
f_rsps_to_core.enq (rsp);
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_handle_read_req: unmapped IO address; returning error response",
cur_cycle);
$display (" ", fshow (req));
end
end
endrule
// ----------------
rule rl_handle_read_rsps;
let mem_rsp <- get(master_shim.slave.r);
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_handle_read_rsps ", cur_cycle);
$display (" ", fshow (mem_rsp));
end
if ((cfg_verbosity > 0) && (mem_rsp.rresp != OKAY)) begin
$display ("%0d: %m.rl_handle_read_rsp: fabric response error", cur_cycle);
$display (" ", fshow (mem_rsp));
end
let newData = rspData;
newData.data[read_req_addr[3]+rg_rd_rsp_beat] = mem_rsp.rdata;
let rsp = MMIODataPRs {valid: (mem_rsp.rresp == OKAY),
data: newData };
if (mem_rsp.rlast) begin
f_rsps_to_core.enq (rsp);
if (cfg_verbosity > 0)
$display (" Response MMIO to core: ", fshow (rsp));
rg_rd_rsp_beat <= 0;
rspData <= unpack(0);
end else begin
rg_rd_rsp_beat <= rg_rd_rsp_beat + 1;
rspData <= newData;
end
endrule
// ================================================================
// Handle write requests and responses
// Each 128b word takes 2 beats, each handling 64 bits
Reg #(Bit #(1)) rg_wr_req_beat <- mkReg (0);
rule rl_handle_write_req (f_reqs_from_core.first.func matches St);
let req = f_reqs_from_core.first;
if (cfg_verbosity > 0) begin
$display ("%d: %m.rl_handle_write_req: St request:", cur_cycle);
$display (" ", fshow (req));
end
AXI4_Size size = byteEnToAxiSize(req.byteEn);
Bool burst = size > 8;
if (burst) size = 8;
Vector #(2, Bit #(8)) line_strb = unpack(pack(req.byteEn));
Bit#(1) whichHalf = rg_wr_req_beat;
if (!burst) whichHalf = (line_strb[0]!=0) ? 0:1;
Bool first = (burst) ? whichHalf==0:True;
Bool last = (burst) ? whichHalf==1:True;
// Technically the following check for legal IO addrs is not
// necessary; the AXI4 fabric should return a DECERR for illegal
// addrs; but not all AXI4 fabrics do the right thing.
if (soc_map.m_is_IO_addr (req.addr, False)) begin
//fa_fabric_send_write_req (req.addr, truncate(pack(req.byteEn)), fromMemTaggedData(req.data));
// on first flit...
// ================
if (first) begin
AXI4_AWFlit #(Wd_CoreW_Bus_MId, Wd_Addr, Wd_AW_User)
mem_req_wr_addr = AXI4_AWFlit {awid: fabric_corew_bus_default_mid,
awaddr: req.addr,
awlen: (burst) ? 1:0, // burst len = awlen+1
awsize: size,
awburst: fabric_default_burst,
awlock: fabric_default_lock,
awcache: fabric_default_awcache,
awprot: fabric_default_prot,
awqos: fabric_default_qos,
awregion: fabric_default_region,
awuser: 0};
master_shim.slave.aw.put (mem_req_wr_addr);
if (cfg_verbosity > 0) begin
$display ("%d: %m.rl_handle_write_req: sent aw flit:", cur_cycle);
$display (" ", fshow (mem_req_wr_addr));
end
// Expect a fabric response
ctr_wr_rsps_pending.incr;
end
// on last flit...
// ===============
if (last) begin
f_reqs_from_core.deq;
rg_wr_req_beat <= 0;
end else // increment flit counter
rg_wr_req_beat <= rg_wr_req_beat + 1;
// on each flit...
// ===============
AXI4_WFlit #(Wd_Data_Periph, Wd_W_User)
wflit = AXI4_WFlit {wdata: req.data.data[whichHalf],
wstrb: line_strb[whichHalf],
wlast: last,
wuser: 0};
master_shim.slave.w.put (wflit);
if (cfg_verbosity > 0) begin
$display ("%d: %m.rl_handle_write_req: sent w flit:", cur_cycle);
$display (" ", fshow (wflit), "first: %d, last: %d, whichHalf: %d", first, last, whichHalf);
end
end else begin
let rsp = MMIODataPRs {valid: False,
data: toMemTaggedData(req.addr)}; // For debugging convenience only
f_reqs_from_core.deq;
f_rsps_to_core.enq (rsp);
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_handle_write_req: unmapped IO address; returning error response",
cur_cycle);
$display (" ", fshow (req));
end
end
endrule
// ----------------
// Discard write-responses from the fabric
rule rl_discard_write_rsp;
let wr_resp <- get(master_shim.slave.b);
if (cfg_verbosity > 0) begin
$display ("%0d: %m.rl_discard_write_rsp", cur_cycle);
$display (" ", fshow (wr_resp));
end
if (ctr_wr_rsps_pending.value == 0) begin
$display ("%0d:%m.rl_discard_write_rsp: ERROR:unexpected Wr response (ctr_wr_rsps_pending.value == 0)",
cur_cycle);
$display (" ", fshow (wr_resp));
$finish (1); // Assertion failure
end
ctr_wr_rsps_pending.decr;
if (wr_resp.bresp != OKAY) begin
// TODO: need to raise a non-maskable interrupt (NMI) here
$display ("%0d:%m.rl_discard_write_rsp: ERROR: fabric response error: exit.", cur_cycle);
$display (" ", fshow (wr_resp));
$finish (1);
end
f_rsps_to_core.enq (MMIODataPRs {valid: wr_resp.bresp == OKAY, data: 0});
endrule
// ================================================================
// This adapter should only receive Ld/St requests, no Inst or AMO reqs.
function Bool fn_is_Ld_or_St (MMIOCRq req);
return case (req.func) matches
Ld : True;
St : True;
default: False;
endcase;
endfunction
rule rl_handle_non_Ld_St (! fn_is_Ld_or_St (f_reqs_from_core.first));
let req <- pop (f_reqs_from_core);
$display ("%0d:%m.rl_handle_non_Ld_St: ERROR: neither Ld nor St? exit.", cur_cycle);
$display (" ", fshow (req));
$finish (1); // Assertion failure
endrule
// ================================================================
// INTERFACE
method Action reset;
ctr_wr_rsps_pending.clear;
endmethod
interface Server core_side = toGPServer (f_reqs_from_core, f_rsps_to_core);
// Fabric master interface for IO
interface mmio_master = master_shim.master;
endmodule
// ================================================================
endpackage