gary/debugger/Debugger.bsv

181 lines
5.6 KiB
Plaintext

package Debugger;
import GetPut::*;
import ClientServer::*;
import FIFOF::*;
import SpecialFIFOs::*;
import VRAM::*;
export ReadRange(..);
export DebugRequest(..);
export DebugResponse(..);
export Debugger(..), mkDebugger;
// ReadRange is a request to read a range of bytes.
typedef struct {
VRAMAddr start;
UInt#(8) count;
} ReadRange deriving (Eq, FShow);
// DebugRequest is a request sent to the debugger.
typedef union tagged {
// Ping is a probe to check the connection to the debugger. The
// debugger responds with 0xEA (Ω in code page 437) to identify
// itself as a GARY debugger. The eschatological symbol for the end
// of everything seems appropriate for the level of desperation
// where you reach for the debug interface.
//
// Did you know Ω featured on the mission patch of STS-135, the
// final flight of the US Space Shuttle program?
//
// "Having fired the imagination of a generation,
// a ship like no other, its place in history secured,
// the space shuttle pulls into port for the last time.
// Its voyage, at an end."
void Ping;
// MemByteOp requests a read or write of a single byte of
// memory. The associated VRAMRequest gets passed unmodified to the
// memory port. For reads, produces a single VRAMResponse with the
// data byte. For writes, no response.
VRAMRequest MemByteOp;
// ReadRange requests a read of a range of memory. It's equivalent
// to N read MemByteOps, but the memory operations are pipelined,
// enabling 3x faster readout.
ReadRange ReadRange;
} DebugRequest deriving (Eq, FShow);
// DebugResponse is the debugger's response to a command. Depending on
// the command, one DebugRequest may result in 0, 1 or many
// DebugResponses. Requests are fully processed and responded to
// before following requests are processed.
typedef VRAMResponse DebugResponse;
// DebugCmd is the value of the Cmd byte in raw encoded RawDebugReq.
typedef enum {
Ping = 'h7F, // To force the enum to be Bit#(7)
ReadByte = 1,
WriteByte = 2,
ReadRange = 3
} DebugCmd deriving (Bits, Eq, FShow);
// RawDebugReq is the bit encoding of a DebugRequest. It is 32 bits
// for all commands, in a format that's reasonably easy to assemble
// from the client side.
typedef struct {
DebugCmd cmd;
VRAMAddr addr;
VRAMData data;
} RawDebugReq deriving (Bits, Eq, FShow);
instance Bits#(DebugRequest, 32);
function Bit#(32) pack(DebugRequest r);
case (r) matches
tagged Ping:
return pack(RawDebugReq{
cmd: Ping,
addr: 0,
data: 0
});
tagged MemByteOp .req:
if (req.data matches tagged Valid .data)
return pack(RawDebugReq{
cmd: WriteByte,
addr: req.addr,
data: data
});
else
return pack(RawDebugReq{
cmd: ReadByte,
addr: req.addr,
data: 0
});
tagged ReadRange .req:
return pack(RawDebugReq{
cmd: ReadRange,
addr: req.start,
data: unpack(pack(req.count))
});
endcase
endfunction
function DebugRequest unpack(Bit#(32) b);
RawDebugReq raw = unpack(b);
case (raw.cmd)
Ping: return tagged Ping;
ReadByte: return tagged MemByteOp VRAMRequest{addr: raw.addr, data: tagged Invalid};
WriteByte: return tagged MemByteOp VRAMRequest{addr: raw.addr, data: tagged Valid raw.data};
ReadRange: return tagged ReadRange ReadRange{start: raw.addr, count: unpack(pack(raw.data))};
endcase
endfunction
endinstance
// A Debugger provides debug access to GARY.
interface Debugger;
interface Server#(DebugRequest, DebugResponse) server;
interface Client#(VRAMRequest, VRAMResponse) vram;
endinterface
// mkDebugger constructs a GARY debugger.
(* descending_urgency="mem_response,issue_range,start_req" *)
module mkDebugger(Debugger);
FIFOF#(DebugRequest) req <- mkPipelineFIFOF();
FIFOF#(DebugResponse) resp <- mkBypassFIFOF();
FIFOF#(VRAMRequest) mem_req <- mkBypassFIFOF();
FIFOF#(VRAMResponse) mem_resp <- mkPipelineFIFOF();
Reg#(VRAMAddr) next_read_addr <- mkReg(0);
Reg#(UInt#(8)) read_issue_cnt <- mkReg(0);
Reg#(UInt#(8)) read_resp_cnt <- mkReg(0);
function Bool busy();
return read_resp_cnt != 0;
endfunction
rule start_req (req.notEmpty && !busy());
case (req.first) matches
tagged Ping: begin
req.deq();
resp.enq(DebugResponse{data: 'hEA});
end
tagged MemByteOp .op: begin
mem_req.enq(op);
req.deq();
if (op.data matches tagged Invalid)
read_resp_cnt <= 1;
end
tagged ReadRange .range: begin
mem_req.enq(VRAMRequest{
addr: range.start,
data: tagged Invalid
});
req.deq();
next_read_addr <= range.start+1;
read_issue_cnt <= range.count-1;
read_resp_cnt <= range.count;
end
endcase
endrule
rule issue_range (read_issue_cnt > 0);
mem_req.enq(VRAMRequest{
addr: next_read_addr,
data: tagged Invalid
});
next_read_addr <= next_read_addr+1;
read_issue_cnt <= read_issue_cnt-1;
endrule
rule mem_response (mem_resp.notEmpty);
resp.enq(mem_resp.first);
mem_resp.deq();
read_resp_cnt <= read_resp_cnt-1;
endrule
interface server = toGPServer(req, resp);
interface vram = toGPClient(mem_req, mem_resp);
endmodule
endpackage