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: 0}); 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