gary/vram/VRAM.bsv

106 lines
3.3 KiB
Plaintext
Raw Normal View History

package VRAM;
import Connectable::*;
import GetPut::*;
import ClientServer::*;
import Vector::*;
import FIFOF::*;
import SpecialFIFOs::*;
import MemArbiter::*;
import VRAMCore::*;
// Re-exports from VRAMCore
export VRAMAddr, VRAMData, VRAMRequest(..), VRAMResponse(..);
export VRAMServer(..);
export VRAM(..), mkVRAM;
// A VRAMServer is a memory port.
typedef Server#(VRAMRequest, VRAMResponse) VRAMServer;
// mkArbitratedVRAMServers expands a VRAMServer port into multiple
// ports through the use of a MemArbiter.
module mkArbitratedVRAMServers(VRAMServer ram, MemArbiter#(n, VRAMAddr) arb, Vector#(n, VRAMServer) ifc)
provisos (Min#(n, 1, 1),
Alias#(port_idx, UInt#(TLog#(n))));
Vector#(n, FIFOF#(VRAMRequest)) requests <- replicateM(mkBypassFIFOF());
Vector#(n, FIFOF#(VRAMResponse)) responses <- replicateM(mkBypassFIFOF());
Reg#(Maybe#(port_idx)) awaiting_response[2] <- mkCReg(2, tagged Invalid);
(* fire_when_enabled *)
rule request_ports;
for (Integer i=0; i<valueOf(n); i=i+1)
if (requests[i].notEmpty) begin
let req = requests[i].first;
let arb_req = MemArbiterOp{
write: isValid(req.data),
addr: req.addr
};
arb.ports[i].request(arb_req);
end
endrule
(* fire_when_enabled *)
rule submit (awaiting_response[1] matches tagged Invalid);
let port = arb.granted_port();
let req = requests[port].first;
ram.request.put(req);
requests[port].deq();
// Only reads generate a response.
if (req.data matches tagged Invalid)
awaiting_response[1] <= tagged Valid port;
endrule
(* fire_when_enabled *)
rule response (awaiting_response[0] matches tagged Valid .port);
let resp <- ram.response.get();
responses[port].enq(resp);
awaiting_response[0] <= tagged Invalid;
endrule
return map(uncurry(toGPServer), zip(requests, responses));
endmodule
// VRAM is a GARY video RAM and its memory ports.
interface VRAM;
interface VRAMServer cpu;
interface VRAMServer debugger;
interface VRAMServer palette;
interface VRAMServer tile1;
interface VRAMServer tile2;
interface VRAMServer sprite;
endinterface
// mkVRAM constructs a VRAM of the requested size. The memory size must be a multiple of
// 4KiB, with a maximum of 128KiB.
//
// Memory accesses are spread across two internal ports as follows:
//
// Port A: strict most-important-wins priority: CPU, then debugger,
// then palette DAC.
// Port B: equal round-robin prioritization between two tile engines
// and the sprite engine.
module mkVRAM(Integer num_kilobytes, VRAM ifc);
VRAMCore ram <- mkVRAMCore(num_kilobytes);
MemArbiter#(3, VRAMAddr) arbA <- mkPriorityMemArbiter();
Vector#(3, VRAMServer) portA <- mkArbitratedVRAMServers(ram.portA, arbA);
MemArbiter#(3, VRAMAddr) arbB <- mkRoundRobinMemArbiter();
Vector#(3, VRAMServer) portB <- mkArbitratedVRAMServers(ram.portB, arbB);
// Connect the arbiters together so they correctly prevent
// write-write and write-read conflicts.
mkConnection(arbA, arbB);
interface cpu = portA[0];
interface debugger = portA[1];
interface palette = portA[2];
interface tile1 = portB[0];
interface tile2 = portB[1];
interface sprite = portB[2];
endmodule
endpackage